[gradle-1.12] 24/211: upstream import 1.0~m3

Kai-Chung Yan seamlik-guest at moszumanska.debian.org
Wed Jul 1 14:17:53 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 34b427b774e2fe84e831743c955bb50a9efcdee5
Author: Miguel Landaeta <miguel at miguel.cc>
Date:   Sun May 1 22:49:34 2011 -0430

    upstream import 1.0~m3
---
 build.gradle                                       | 194 ++---
 buildSrc/build.gradle                              |   4 +-
 .../build/docs/dsl/docbook/JavadocScanner.java     |   2 +-
 .../org/gradle/build/docs/XmlSpecification.groovy  |   2 +-
 .../docs/dsl/docbook/JavadocConverterTest.groovy   |  12 +
 config/checkstyle/checkstyle-api.xml               |   5 +-
 config/codenarc.xml                                |   6 +-
 gradle.properties                                  |   4 +-
 gradle/codeQuality.gradle                          |   4 +-
 gradle/compile.gradle                              |  11 +-
 gradle/integTest.gradle                            |  72 ++
 gradle/publish.gradle                              |  20 +-
 gradle/ssh.gradle                                  |  54 ++
 gradle/wrapper/gradle-wrapper.properties           |   4 +-
 gradlew                                            |  33 +-
 gradlew.bat                                        |  60 +-
 settings.gradle                                    |   6 +-
 src/toplevel/NOTICE                                |   3 +-
 src/toplevel/changelog.txt                         |   4 +-
 .../api/plugins/announce/AnnouncePlugin.groovy     |  30 +-
 .../org/gradle/api/plugins/announce/Announcer.java |   2 +-
 .../announce/internal/AnnouncerFactory.groovy      |  25 +
 .../announce/internal/AnnouncerFactory.java        |  25 -
 .../internal/DefaultAnnouncerFactory.groovy        |  35 +-
 .../api/plugins/announce/internal/Growl.groovy     |  17 +-
 .../plugins/announce/internal/NotifySend.groovy    |  24 +-
 .../api/plugins/announce/internal/Snarl.groovy     |  78 +-
 .../api/plugins/announce/internal/Twitter.groovy   |  65 +-
 .../gradle/api/plugins/announce/package-info.java  |   2 +-
 .../META-INF/gradle-plugins/announce.properties    |  15 -
 .../internal/DefaultAnnouncerFactoryTest.groovy    |   5 +-
 .../announce/internal/NotifySendTest.groovy        |  60 +-
 .../api/plugins/announce/internal/SnarlTest.groovy |  14 +-
 .../org/gradle/api/plugins/antlr/AntlrPlugin.java  |   2 +-
 .../plugins/antlr/AntlrSourceVirtualDirectory.java |   7 -
 .../internal/AntlrSourceVirtualDirectoryImpl.java  |  17 +-
 .../org/gradle/api/plugins/antlr/package-info.java |   2 +-
 .../META-INF/gradle-plugins/antlr.properties       |  15 -
 subprojects/code-quality/code-quality.gradle       |   4 +-
 .../api/plugins/quality/AntCheckstyle.groovy       |  42 -
 .../gradle/api/plugins/quality/AntCodeNarc.groovy  |  42 -
 .../org/gradle/api/plugins/quality/Checkstyle.java |   1 +
 .../org/gradle/api/plugins/quality/CodeNarc.java   |  29 +-
 .../api/plugins/quality/CodeQualityPlugin.groovy   |  67 +-
 .../GroovyCodeQualityPluginConvention.groovy       |   7 +
 .../plugins/quality/internal/AntCheckstyle.groovy  |  42 +
 .../plugins/quality/internal/AntCodeNarc.groovy    |  45 +
 .../quality/internal/ConsoleReportWriter.java      |  53 ++
 .../gradle-plugins/code-quality.properties         |  17 +-
 .../plugins/quality/CodeQualityPluginTest.groovy   |   2 +
 subprojects/core-impl/core-impl.gradle             |  17 +
 .../DefaultDependencyManagementServices.java       |  39 +
 .../artifacts/publish/maven/ArtifactPom.java       |  41 +
 .../publish/maven/ArtifactPomContainer.java        |  31 +
 .../publish/maven/ArtifactPomFactory.java          |  25 +
 .../publish/maven/DefaultArtifactPomFactory.java   |  28 +
 .../maven/DefaultLocalMavenCacheLocator.java       |  95 +++
 .../publish/maven/DefaultMavenFactory.java         |  46 ++
 .../artifacts/publish/maven/DefaultMavenPom.java   | 252 ++++++
 .../publish/maven/DefaultMavenPomFactory.java      |  47 ++
 .../publish/maven/ExcludeRuleConverter.java        |  26 +
 .../publish/maven/LocalMavenCacheLocator.java      |  22 +
 .../publish/maven/PomDependenciesConverter.java    |  29 +
 .../DefaultConf2ScopeMappingContainer.java         |   0
 .../dependencies/DefaultExcludeRuleConverter.java  |  40 +
 .../DefaultPomDependenciesConverter.java           | 149 ++++
 .../maven/deploy/AbstractMavenResolver.java        | 295 +++++++
 .../publish/maven/deploy/BaseMavenDeployer.java    | 129 +++
 .../publish/maven/deploy/BaseMavenInstaller.java   |  51 ++
 .../maven/deploy/BasePomFilterContainer.java       | 137 ++++
 .../publish/maven/deploy/ClassifierArtifact.java   |   0
 .../publish/maven/deploy/CustomDeployTask.java     |   0
 .../deploy/CustomInstallDeployTaskSupport.java     |   0
 .../publish/maven/deploy/CustomInstallTask.java    |   0
 .../publish/maven/deploy/DefaultArtifactPom.java   | 213 +++++
 .../maven/deploy/DefaultArtifactPomContainer.java  |  78 ++
 .../maven/deploy/DefaultDeployTaskFactory.java     |   0
 .../maven/deploy/DefaultInstallTaskFactory.java    |   0
 .../maven/deploy/DefaultMavenDeployment.java       |   0
 .../publish/maven/deploy/DefaultPomFilter.java     |   0
 .../publish/maven/deploy/LoggingHelper.java        |   0
 .../groovy/DefaultGroovyMavenDeployer.groovy       |  51 ++
 .../maven/deploy/groovy/RepositoryBuilder.java     |   0
 .../maven/deploy/groovy/RepositoryFactory.java     |   0
 .../mvnsettings/EmptyMavenSettingsSupplier.java    |  49 ++
 .../deploy/mvnsettings/MavenSettingsProvider.java  |  39 +
 .../deploy/mvnsettings/MavenSettingsSupplier.java  |  27 +
 .../MaybeUserMavenSettingsSupplier.java            |  45 +
 .../maven/pombuilder/CustomModelBuilder.java       |   0
 .../publish/maven/pombuilder/ModelFactory.java     |   0
 .../maven/pombuilder/PlexusLoggerAdapter.java      |   0
 .../CommonsHttpClientBackedRepository.java         | 255 ++++++
 .../repositories/DefaultIvyArtifactRepository.java |  99 +++
 .../repositories/DefaultResolverFactory.java       | 166 ++++
 .../DependencyManagementServicesTest.groovy        |  40 +
 .../maven/DefaultDeployTaskFactoryTest.java        |  31 +
 .../maven/DefaultLocalMavenCacheLocatorTest.groovy |  58 ++
 .../maven/DefaultMavenPomFactoryTest.groovy        |  46 ++
 .../publish/maven/DefaultMavenPomTest.groovy       | 191 +++++
 .../DefaultConf2ScopeMappingContainerTest.java     |   0
 .../DefaultExcludeRuleConverterTest.java           |   0
 .../DefaultPomDependenciesConverterTest.java       | 254 ++++++
 .../maven/deploy/AbstractMavenResolverTest.java    | 239 ++++++
 .../maven/deploy/BaseMavenDeployerTest.java        | 106 +++
 .../maven/deploy/BaseMavenInstallerTest.java       |  71 ++
 .../maven/deploy/BasePomFilterContainerTest.java   |   0
 .../deploy/DefaultArtifactPomContainerTest.groovy  | 100 +++
 .../maven/deploy/DefaultArtifactPomTest.java       | 272 ++++++
 .../publish/maven/deploy/DefaultPomFilterTest.java |   0
 .../groovy/DefaultGroovyMavenDeployerTest.groovy   | 117 +++
 .../DefaultGroovyPomFilterContainerTest.groovy     | 122 +++
 .../EmptyMavenSettingsSupplierTest.groovy          |  55 ++
 .../MaybeUserMavenSettingsSupplierTest.groovy      |  63 ++
 .../DefaultIvyArtifactRepositoryTest.groovy        |  57 ++
 .../repositories/DefaultResolverFactoryTest.groovy | 127 +++
 subprojects/core/core.gradle                       |  24 +-
 .../integtests/AbstractIdeIntegrationTest.groovy   |  54 --
 .../gradle/integtests/AbstractIntegrationTest.java |  77 --
 .../integtests/AntProjectIntegrationTest.groovy    | 158 ----
 .../gradle/integtests/AntlrIntegrationTest.java    |  26 -
 .../integtests/ArchiveIntegrationTest.groovy       | 661 ---------------
 .../ArtifactDependenciesIntegrationTest.groovy     | 168 ----
 .../BuildAggregationIntegrationTest.groovy         |  96 ---
 .../BuildScriptClasspathIntegrationTest.java       | 184 -----
 .../BuildScriptErrorIntegrationTest.java           | 182 ----
 .../BuildScriptExecutionIntegrationTest.groovy     |  81 --
 .../integtests/CacheProjectIntegrationTest.groovy  | 123 ---
 .../integtests/CodeQualityIntegrationTest.groovy   | 187 -----
 .../integtests/CommandLineIntegrationTest.groovy   | 130 ---
 .../integtests/CopyErrorIntegrationTest.groovy     |  75 --
 .../integtests/CopyTaskIntegrationTest.groovy      | 320 --------
 ...CrossVersionCompatibilityIntegrationTest.groovy |  82 --
 .../integtests/DistributionIntegrationTest.groovy  | 127 ---
 .../integtests/EclipseIntegrationTest.groovy       | 126 ---
 .../gradle/integtests/ExecIntegrationTest.groovy   |  41 -
 .../ExternalPluginIntegrationTest.groovy           |  68 --
 .../ExternalScriptErrorIntegrationTest.groovy      |  89 --
 .../ExternalScriptExecutionIntegrationTest.groovy  | 184 -----
 .../integtests/FileTreeCopyIntegrationTest.groovy  |  82 --
 .../integtests/GroovyProjectIntegrationTest.java   |  37 -
 .../gradle/integtests/IdeaIntegrationTest.groovy   | 144 ----
 .../IncrementalBuildIntegrationTest.groovy         | 378 ---------
 .../IncrementalGroovyCompileIntegrationTest.groovy |  53 --
 ...ementalGroovyProjectBuildIntegrationTest.groovy |  55 --
 .../IncrementalScalaCompileIntegrationTest.groovy  |  53 --
 .../integtests/InitScriptErrorIntegrationTest.java |  47 --
 .../InitScriptExecutionIntegrationTest.groovy      |  87 --
 .../integtests/IvyPublishIntegrationTest.java      |  43 -
 .../gradle/integtests/JUnitIntegrationTest.groovy  | 377 ---------
 .../integtests/JUnitTestExecutionResult.groovy     | 153 ----
 .../integtests/JavaProjectIntegrationTest.java     | 104 ---
 .../integtests/LoggingIntegrationTest.groovy       | 343 --------
 .../integtests/MultiprojectIntegrationTest.groovy  |  47 --
 .../OsgiProjectSampleIntegrationTest.groovy        |  59 --
 .../integtests/ProjectLoadingIntegrationTest.java  | 253 ------
 .../SamplesJavaOnlyIfIntegrationTest.groovy        |  92 ---
 .../SamplesJavaQuickstartIntegrationTest.groovy    |  70 --
 .../integtests/ScalaProjectIntegrationTest.java    |  37 -
 .../SettingsScriptErrorIntegrationTest.java        |  40 -
 .../SettingsScriptExecutionIntegrationTest.groovy  |  68 --
 .../integtests/SyncTaskIntegrationTest.groovy      |  54 --
 .../TaskAutoDependencyIntegrationTest.groovy       |  71 --
 .../integtests/TaskDefinitionIntegrationTest.java  | 135 ---
 .../integtests/TaskExecutionIntegrationTest.java   | 153 ----
 .../UserGuideSamplesIntegrationTest.groovy         |  23 -
 .../integtests/UserGuideSamplesRunner.groovy       | 261 ------
 .../integtests/WaterProjectIntegrationTest.groovy  |  77 --
 .../integtests/WebProjectIntegrationTest.java      |  80 --
 .../WrapperProjectIntegrationTest.groovy           |  45 -
 .../integtests/fixtures/DaemonGradleExecuter.java  |  12 +-
 .../integtests/fixtures/ExecutionFailure.java      |   2 +
 .../integtests/fixtures/ExecutionResult.java       |  10 +
 .../integtests/fixtures/ForkingGradleExecuter.java |  37 +-
 .../integtests/fixtures/GradleDistribution.java    |   4 +-
 .../fixtures/GradleDistributionExecuter.java       |  25 +-
 .../gradle/integtests/fixtures/HttpServer.groovy   | 205 ++++-
 .../fixtures/InProcessGradleExecuter.java          |  29 +-
 .../integtests/fixtures/IvyRepository.groovy       |  81 ++
 .../integtests/fixtures/MavenRepository.groovy     |  91 ++
 .../fixtures/PreviousGradleVersionExecuter.groovy  |   6 +-
 .../integtests/fixtures/ScriptExecuter.groovy      |  38 +
 .../fixtures/TestClassExecutionResult.java         |  10 +
 .../internal/AbstractAutoTestedSamplesTest.groovy  |  33 +
 .../fixtures/internal/AbstractIntegrationTest.java |  77 ++
 .../fixtures/internal/AutoTestedSamplesUtil.groovy |  70 ++
 .../fixtures/internal/IntegrationTestHint.java     |  31 +
 .../maven/MavenProjectIntegrationTest.groovy       |  63 --
 .../gradle/integtests/maven/MavenRepository.groovy |  48 --
 .../maven/MavenSnapshotIntegrationTest.groovy      |  97 ---
 ...SamplesMavenPomGenerationIntegrationTest.groovy | 152 ----
 .../SamplesMavenQuickstartIntegrationTest.groovy   |  96 ---
 .../integtests/testng/TestNGExecutionResult.groovy | 157 ----
 .../SamplesToolingApiIntegrationTest.groovy        |  45 -
 .../ToolingApiEclipseIntegrationTest.groovy        |  80 --
 .../tooling/ToolingApiIntegrationTest.groovy       |  79 --
 .../tooling/ToolingApiSpecification.groovy         |  31 -
 .../CommandLineIntegrationTest/shared/build.gradle |  16 -
 .../src/integTest/java/org/gradle/SomeClass.java   |   3 -
 .../expectedFiles/apiClasspath.xml                 |  12 -
 .../expectedFiles/apiProject.xml                   |  15 -
 .../expectedFiles/groovyprojectProject.xml         |  16 -
 .../expectedFiles/javabaseprojectProject.xml       |  15 -
 .../expectedFiles/masterProject.xml                |   8 -
 .../expectedFiles/webAppJava6Project.xml           |  25 -
 .../expectedFiles/webAppJava6WtpComponent.xml      |   8 -
 .../expectedFiles/webAppWithVarsProject.xml        |  25 -
 .../expectedFiles/webAppWithVarsWtpComponent.xml   |  11 -
 .../expectedFiles/webserviceProject.xml            |  25 -
 .../expectedFiles/webserviceWtpComponent.xml       |  14 -
 .../canCreateAndDeleteMetaData/master/build.gradle |  99 ---
 .../webAppJava6/src/main/webapp/index.html         |   1 -
 .../webAppWithVars/build.gradle                    |   9 -
 .../expectedFiles/api/api.iml.xml                  |  40 -
 .../expectedFiles/root.iml.xml                     |  12 -
 .../expectedFiles/webservice/webservice.iml.xml    |  76 --
 .../webservice/build.gradle                        |  15 -
 .../expectedFiles/root.iml.xml                     |  39 -
 .../overwritesExistingDependencies/root.iml        |  20 -
 .../build.gradle                                   |   4 -
 .../settings.gradle                                |   3 -
 .../expectedFiles/root.iml.xml                     |  12 -
 .../expectedFiles/root/root.iml.xml                |  18 -
 .../expectedFiles/top-level.iml.xml                |  18 -
 .../build.gradle                                   |  14 -
 .../shared/build.gradle                            |   9 -
 .../canHaveMultipleTestTaskInstances/build.gradle  |  21 -
 .../canRunJunit3Tests/build.gradle                 |   9 -
 .../src/test/java/org/gradle/Test1.java            |   9 -
 .../canRunSingleTests/build.gradle                 |   9 -
 .../detectsTestClasses/build.gradle                |   8 -
 .../executesTestsInCorrectEnvironment/build.gradle |   8 -
 .../src/test/java/org/gradle/OkTest.java           |  72 --
 .../build.gradle                                   |   3 -
 .../logging/buildSrc/build.gradle                  |  22 -
 .../LoggingIntegrationTest/logging/external.gradle |   9 -
 .../LoggingIntegrationTest/logging/init.gradle     |  46 --
 .../logging/nestedBuild/buildSrc/build.gradle      |  22 -
 .../canUseANonStandardBuildDir/build.gradle        |  13 -
 .../integtests/copyTestResources/src/two/two.a     |   3 -
 .../settings.gradle                                |   1 -
 .../canListenForTestResults/build.gradle           |  20 -
 .../executesTestsInCorrectEnvironment/build.gradle |   9 -
 .../groovyJdk15Failing/build.gradle                |  17 -
 .../groovyJdk15Passing/build.gradle                |  17 -
 .../javaJdk15Failing/build.gradle                  |  15 -
 .../groovy/org/gradle/BuildExceptionReporter.java  |  95 ++-
 .../src/main/groovy/org/gradle/GradleLauncher.java |   9 +-
 .../groovy/org/gradle/TaskExecutionLogger.java     |  16 +-
 .../org/gradle/api/GradleScriptException.java      |   5 -
 .../main/groovy/org/gradle/api/JavaVersion.java    |   2 +-
 .../org/gradle/api/LocationAwareException.java     |  89 +-
 .../src/main/groovy/org/gradle/api/Project.java    |  16 +-
 .../core/src/main/groovy/org/gradle/api/Task.java  |   2 +
 .../org/gradle/api/artifacts/Configuration.java    |  23 +-
 .../org/gradle/api/artifacts/PublishException.java |  30 +
 .../org/gradle/api/artifacts/ResolveException.java |   4 -
 .../api/artifacts/ResolvedConfiguration.java       |  11 +
 .../gradle/api/artifacts/ResolverContainer.java    |   1 -
 .../api/artifacts/dsl/ArtifactRepository.java      |  35 +
 .../api/artifacts/dsl/IvyArtifactRepository.java   |  56 ++
 .../api/artifacts/dsl/RepositoryHandler.java       |  20 +-
 .../gradle/api/artifacts/maven/MavenDeployer.java  |  14 +-
 .../api/artifacts/maven/MavenDeployment.java       |   7 +
 .../gradle/api/artifacts/maven/MavenFactory.java   |  34 +
 .../org/gradle/api/artifacts/maven/MavenPom.java   |  10 +-
 .../gradle/api/artifacts/maven/MavenResolver.java  |   3 +-
 .../api/artifacts/specs/DependencySpecs.java       |   4 +
 .../org/gradle/api/artifacts/specs/Type.java       |  18 +-
 .../org/gradle/api/dsl/ConvenienceProperty.java    |  73 ++
 .../groovy/org/gradle/api/dsl/package-info.java    |  20 +
 .../api/file/ConfigurableFileCollection.java       |  24 +-
 .../org/gradle/api/file/ConfigurableFileTree.java  |   2 +-
 .../main/groovy/org/gradle/api/file/CopySpec.java  | 493 +++++------
 .../groovy/org/gradle/api/file/DirectoryTree.java  |  40 +
 .../org/gradle/api/file/SourceDirectorySet.java    |  17 +-
 .../api/internal/AbstractClassPathProvider.java    |  14 +-
 .../api/internal/AbstractMultiCauseException.java  |  77 ++
 .../org/gradle/api/internal/AbstractTask.java      |  31 +-
 .../groovy/org/gradle/api/internal/Contextual.java |   3 -
 .../DefaultAutoCreateDomainObjectContainer.java    |   4 +-
 .../api/internal/DefaultClassPathProvider.java     |  72 +-
 .../gradle/api/internal/MultiCauseException.java   |  22 +
 .../org/gradle/api/internal/TaskInternal.java      |   9 +-
 .../org/gradle/api/internal/XmlTransformer.java    |   9 +-
 .../artifacts/DependencyManagementServices.java    |  29 +
 .../gradle/api/internal/artifacts/IvyService.java  |   3 +-
 .../configurations/DefaultConfiguration.java       |  18 -
 .../dependencies/DefaultProjectDependency.java     |   9 +-
 .../artifacts/dsl/DefaultRepositoryHandler.java    |  35 +
 .../SharedConventionRepositoryHandlerFactory.java  |   4 +-
 .../artifacts/ivyservice/ClientModuleResolver.java |  42 +-
 .../ivyservice/DefaultIvyDependencyResolver.java   |  26 +-
 .../ivyservice/DefaultResolverFactory.java         | 188 -----
 .../ivyservice/DefaultSettingsConverter.java       |  41 +-
 .../ivyservice/ErrorHandlingIvyService.java        |  24 +-
 .../ivyservice/GradleIBiblioResolver.java          |  37 +-
 .../LocalFileRepositoryCacheManager.java           |  93 +++
 .../ivyservice/LocalMavenCacheLocator.java         |  94 ---
 .../ivyservice/NoOpRepositoryCacheManager.java     |  76 ++
 .../artifacts/ivyservice/ResolverFactory.java      |   3 +
 .../SelfResolvingDependencyResolver.java           |   4 +
 .../artifacts/ivyservice/SettingsConverter.java    |   3 +-
 .../ShortcircuitEmptyConfigsIvyService.java        |   4 +
 .../publish/maven/DefaultArtifactPomFactory.java   |  30 -
 .../artifacts/publish/maven/DefaultMavenPom.java   | 251 ------
 .../publish/maven/DefaultMavenPomFactory.java      |  48 --
 .../dependencies/DefaultExcludeRuleConverter.java  |  39 -
 .../DefaultPomDependenciesConverter.java           | 147 ----
 .../maven/dependencies/ExcludeRuleConverter.java   |  27 -
 .../dependencies/PomDependenciesConverter.java     |  29 -
 .../maven/dependencies/PomDependenciesWriter.java  |  31 -
 .../maven/deploy/AbstractMavenResolver.java        | 304 -------
 .../publish/maven/deploy/ArtifactPom.java          |  41 -
 .../publish/maven/deploy/ArtifactPomContainer.java |  30 -
 .../publish/maven/deploy/ArtifactPomFactory.java   |  25 -
 .../publish/maven/deploy/BaseMavenDeployer.java    | 128 ---
 .../publish/maven/deploy/BaseMavenInstaller.java   |  48 --
 .../maven/deploy/BasePomFilterContainer.java       | 137 ----
 .../publish/maven/deploy/DefaultArtifactPom.java   | 212 -----
 .../maven/deploy/DefaultArtifactPomContainer.java  |  74 --
 .../groovy/DefaultGroovyMavenDeployer.groovy       |  51 --
 .../repositories/ArtifactRepositoryInternal.java   |  30 +
 .../repositories/DefaultInternalRepository.java    |   2 +
 .../CacheBackedFileSnapshotRepository.java         |  55 ++
 .../CacheBackedTaskHistoryRepository.java          | 170 ++++
 .../changedetection/CompositeUpToDateRule.java     |  52 ++
 .../changedetection/DefaultFileCacheListener.java  |  45 +
 .../changedetection/DefaultFileSnapshotter.java    | 316 +++----
 .../DefaultTaskArtifactStateRepository.java        | 269 ++----
 ...eCacheBroadcastTaskArtifactStateRepository.java |  63 ++
 .../changedetection/FileCacheListener.java         |  35 +
 .../changedetection/FileSnapshotRepository.java    |  24 +
 .../internal/changedetection/FileSnapshotter.java  |  70 +-
 .../changedetection/InMemoryIndexedCache.java      |  61 ++
 .../InputFilesChangedUpToDateRule.java             |  63 ++
 .../InputPropertiesChangedUpToDateRule.java        |  55 ++
 .../OutputFilesChangedUpToDateRule.java            |  85 ++
 .../changedetection/OutputFilesSnapshotter.java    |   4 +-
 .../ShortCircuitTaskArtifactStateRepository.java   | 102 +--
 .../changedetection/TaskArtifactState.java         |  21 +-
 .../internal/changedetection/TaskExecution.java    |  67 ++
 .../changedetection/TaskHistoryRepository.java     |  30 +
 .../TaskTypeChangedUpToDateRule.java               |  42 +
 .../api/internal/changedetection/UpToDateRule.java |  48 ++
 .../api/internal/file/AbstractFileCollection.java  |  34 +-
 .../api/internal/file/AbstractFileResolver.java    | 419 +++++-----
 .../gradle/api/internal/file/AbstractFileTree.java |  12 +-
 .../AntFileCollectionMatchingTaskBuilder.groovy    |  10 +-
 .../api/internal/file/AntFileSetBuilder.groovy     |  38 +
 .../api/internal/file/AntFileTreeBuilder.groovy    |   1 +
 .../api/internal/file/CompositeFileCollection.java |  39 +-
 .../api/internal/file/CompositeFileTree.java       |  14 +-
 .../internal/file/DefaultConfigurableFileTree.java | 221 -----
 .../api/internal/file/DefaultDirectoryWalker.java  | 149 ----
 .../api/internal/file/DefaultFileOperations.java   | 295 +++----
 .../internal/file/DefaultSourceDirectorySet.java   | 102 ++-
 .../gradle/api/internal/file/DirectoryWalker.java  |  26 -
 .../gradle/api/internal/file/FileSetHelper.groovy  |  26 -
 .../org/gradle/api/internal/file/MapFileTree.java  | 154 ----
 .../internal/file/PathResolvingFileCollection.java | 145 ----
 .../api/internal/file/SimpleFileCollection.java    |  50 --
 .../api/internal/file/SingletonFileCollection.java |  52 --
 .../api/internal/file/SingletonFileTree.java       |  70 --
 .../api/internal/file/UnionFileCollection.java     |  21 +-
 .../gradle/api/internal/file/UnionFileTree.java    |   8 +-
 .../api/internal/file/archive/TarFileTree.java     |  30 +-
 .../api/internal/file/archive/ZipFileTree.java     |  35 +-
 ...pendenciesOnlyFileCollectionResolveContext.java |  60 ++
 .../DefaultConfigurableFileCollection.java         |  95 +++
 .../collections/DefaultConfigurableFileTree.java   | 178 ++++
 .../DefaultFileCollectionResolveContext.java       | 224 +++++
 .../file/collections/DirectoryFileTree.java        | 192 +++++
 .../internal/file/collections/EmptyFileTree.java   |  49 ++
 .../file/collections/FileCollectionAdapter.java    |  56 ++
 .../file/collections/FileCollectionContainer.java  |  28 +
 .../collections/FileCollectionResolveContext.java  |  54 ++
 .../collections/FileSystemMirroringFileTree.java   |  27 +
 .../internal/file/collections/FileTreeAdapter.java |  99 +++
 .../file/collections/ListBackedFileSet.java        |  51 ++
 .../internal/file/collections/LocalFileTree.java   |  25 +
 .../api/internal/file/collections/MapFileTree.java | 153 ++++
 .../file/collections/MinimalFileCollection.java    |  28 +
 .../internal/file/collections/MinimalFileSet.java  |  26 +
 .../internal/file/collections/MinimalFileTree.java |  34 +
 .../collections/PatternFilterableFileTree.java     |  25 +
 .../collections/RandomAccessFileCollection.java    |  25 +
 .../ResolvableFileCollectionResolveContext.java    |  33 +
 .../file/collections/SimpleFileCollection.java     |  29 +
 .../file/collections/SingletonFileTree.java        |  52 ++
 .../api/internal/file/copy/CopyActionImpl.java     | 480 +++++------
 .../api/internal/file/copy/CopySpecImpl.java       | 913 +++++++++++----------
 .../internal/file/copy/FileCopySpecVisitor.java    | 114 +--
 .../gradle/api/internal/file/copy/LineFilter.java  | 218 ++---
 .../file/copy/NormalizingCopySpecVisitor.java      |  23 +-
 .../api/internal/file/copy/ReadableCopySpec.java   |   2 +
 .../internal/file/copy/SyncCopySpecVisitor.java    |   8 +-
 .../DefaultScriptHandlerFactory.java               | 178 ++--
 .../org/gradle/api/internal/plugins/IdePlugin.java |  66 --
 .../api/internal/project/AbstractProject.java      |  31 +-
 .../internal/project/DefaultServiceRegistry.java   |   8 +-
 .../internal/project/GlobalServicesRegistry.java   |   2 +-
 .../project/ProjectInternalServiceRegistry.java    |   2 +-
 .../api/internal/project/ServiceRegistry.java      |   2 +-
 .../project/TaskInternalServiceRegistry.java       |   2 +-
 .../project/TopLevelBuildServiceRegistry.java      |  55 +-
 .../AnnotationProcessingTaskFactory.java           |  75 +-
 .../ExecutionShortCircuitTaskExecuter.java         |  58 --
 .../InputDirectoryPropertyAnnotationHandler.java   |  32 +-
 .../InputFilePropertyAnnotationHandler.java        |  13 +-
 .../InputFilesPropertyAnnotationHandler.java       |  20 +-
 .../OutputDirectoryPropertyAnnotationHandler.java  |  41 +-
 .../OutputFilePropertyAnnotationHandler.java       |  42 +-
 .../PostExecutionAnalysisTaskExecuter.java         |  48 --
 .../project/taskfactory/PropertyActionContext.java |   6 -
 .../internal/project/taskfactory/TaskFactory.java  |   4 +
 .../project/taskfactory/ValidationAction.java      |   4 +-
 .../api/internal/tasks/DefaultTaskExecuter.java    |  73 --
 .../api/internal/tasks/DefaultTaskInputs.java      |  39 +-
 .../api/internal/tasks/DefaultTaskOutputs.java     |   8 +-
 .../tasks/ExecuteAtMostOnceTaskExecuter.java       |  34 -
 .../api/internal/tasks/SkipTaskExecuter.java       |  59 --
 .../api/internal/tasks/TaskStateInternal.java      |   4 +-
 .../execution/ExecuteActionsTaskExecuter.java      |  78 ++
 .../execution/ExecuteAtMostOnceTaskExecuter.java   |  48 ++
 .../PostExecutionAnalysisTaskExecuter.java         |  39 +
 .../SkipEmptySourceFilesTaskExecuter.java          |  43 +
 .../tasks/execution/SkipOnlyIfTaskExecuter.java    |  54 ++
 .../execution/SkipTaskWithNoActionsExecuter.java   |  53 ++
 .../tasks/execution/SkipUpToDateTaskExecuter.java  |  66 ++
 .../internal/tasks/execution/TaskValidator.java    |  27 +
 .../tasks/execution/ValidatingTaskExecuter.java    |  59 ++
 .../AbstractPersistableConfigurationObject.java    |  67 --
 .../api/internal/tasks/generator/Generator.java    |  32 -
 .../generator/PersistableConfigurationObject.java  |  26 -
 .../PersistableConfigurationObjectGenerator.java   |  44 -
 .../PropertiesPersistableConfigurationObject.java  |  48 --
 .../XmlPersistableConfigurationObject.java         |  60 --
 .../groovy/org/gradle/api/invocation/Gradle.java   |   1 -
 .../org/gradle/api/logging/LoggingManager.java     |   7 +
 .../org/gradle/api/tasks/AbstractCopyTask.java     |  14 +
 .../main/groovy/org/gradle/api/tasks/Delete.java   |   7 +-
 .../src/main/groovy/org/gradle/api/tasks/Exec.java |  13 +-
 .../groovy/org/gradle/api/tasks/GeneratorTask.java | 163 ----
 .../main/groovy/org/gradle/api/tasks/JavaExec.java |   7 +-
 .../gradle/api/tasks/TaskExecutionException.java   |   5 -
 .../groovy/org/gradle/api/tasks/TaskInputs.java    |  44 +-
 .../groovy/org/gradle/api/tasks/TaskOutputs.java   |   2 +-
 .../gradle/api/tasks/TaskValidationException.java  |  32 +
 .../org/gradle/api/tasks/XmlGeneratorTask.java     |  74 --
 .../org/gradle/api/tasks/util/PatternSet.groovy    |  13 +-
 .../org/gradle/cache/DefaultCacheRepository.java   |   2 +-
 .../configuration/DefaultScriptPluginFactory.java  |   4 +-
 .../main/groovy/org/gradle/configuration/Help.java |   4 +-
 .../scripts/DefaultScriptCompilationHandler.java   |  41 +-
 .../gradle/initialization/BuildProgressLogger.java |  12 +-
 .../gradle/initialization/ClassLoaderFactory.java  |  65 +-
 .../initialization/DefaultClassLoaderFactory.java  | 118 +--
 .../DefaultCommandLineConverter.java               |   6 +-
 .../initialization/DefaultExceptionAnalyser.java   |  34 +-
 .../initialization/DefaultGradleLauncher.java      |  10 +
 .../DefaultGradleLauncherFactory.java              |   8 +-
 .../ExceptionDecoratingClassGenerator.java         | 230 ------
 .../initialization/GradleLauncherAction.java       |  25 +
 .../org/gradle/invocation/DefaultGradle.java       |   2 +-
 .../listener/ListenerNotificationException.java    |   4 -
 .../org/gradle/logging/LoggingServiceRegistry.java |  54 +-
 .../groovy/org/gradle/logging/ProgressLogger.java  |  86 +-
 .../org/gradle/logging/ProgressLoggerFactory.java  |   9 +-
 .../logging/internal/AbstractStyledTextOutput.java |   4 +-
 .../org/gradle/logging/internal/AnsiConsole.java   |   4 +-
 .../internal/ConsoleBackedProgressRenderer.java    |  39 +-
 .../logging/internal/DefaultLoggingManager.java    |  15 +-
 .../internal/DefaultProgressLoggerFactory.java     |  92 ++-
 .../internal/LoggingCommandLineConverter.java      |   9 +-
 .../logging/internal/OutputEventRenderer.java      |  28 +-
 .../internal/ProgressLogEventGenerator.java        |  68 +-
 .../logging/internal/ProgressStartEvent.java       |  20 +-
 .../StreamBackedStandardOutputListener.java        |  51 ++
 .../internal/StreamingStyledTextOutput.java        |  11 +-
 .../internal/StyledTextOutputBackedRenderer.java   |   4 +-
 .../gradle/logging/internal/TerminalDetector.java  |  40 +-
 .../actor/internal/DefaultActorFactory.java        |   3 +
 .../remote/internal/SocketConnection.java          |   2 +-
 .../remote/internal/TcpIncomingConnector.java      |   2 +
 .../groovy/org/gradle/process/BaseExecSpec.java    |  12 +-
 .../main/groovy/org/gradle/process/ExecResult.java |  10 +-
 .../gradle/process/internal/DefaultExecHandle.java |   2 +-
 .../process/internal/DefaultJavaForkOptions.java   | 456 +++++-----
 .../process/internal/DefaultWorkerProcess.java     |  10 +-
 .../internal/DefaultWorkerProcessFactory.java      |  15 +-
 .../org/gradle/process/internal/ExecHandle.java    |   5 +
 .../process/internal/JavaExecHandleBuilder.java    |   4 +-
 .../child/ImplementationClassLoaderWorker.java     |   2 +-
 .../org/gradle/testfixtures/ProjectBuilder.java    | 151 +---
 .../testfixtures/internal/GlobalTestServices.java  |  76 ++
 .../internal/InMemoryCacheFactory.java             |  56 ++
 .../testfixtures/internal/NoOpLoggingManager.java  |  91 ++
 .../internal/TestTopLevelBuildServiceRegistry.java |  47 ++
 .../main/groovy/org/gradle/util/ClasspathUtil.java |   7 +-
 .../org/gradle/util/DistributionLocator.java       |  46 ++
 .../src/main/groovy/org/gradle/util/GUtil.java     |  72 +-
 .../main/groovy/org/gradle/util/GradleVersion.java |  38 +-
 .../core/src/main/groovy/org/gradle/util/Jvm.java  |   4 +
 .../org/gradle/util/LineBufferingOutputStream.java |   2 +-
 .../main/groovy/org/gradle/util/NameMatcher.java   |  20 +-
 .../groovy/org/gradle/util/OperatingSystem.java    |  35 +
 .../groovy/org/gradle/util/SystemProperties.java   |  29 +
 .../src/main/groovy/org/gradle/util/TextUtil.java  |  34 +-
 .../org/gradle/configuration/default-imports.txt   |   2 +
 .../org/gradle/BuildExceptionReporterTest.groovy   |  49 +-
 .../groovy/org/gradle/TaskExecutionLoggerTest.java |  30 +-
 .../groovy/org/gradle/api/GeneratorTaskTest.groovy | 110 ---
 .../groovy/org/gradle/api/JavaVersionTest.java     |   8 +-
 .../api/artifacts/specs/DependencySpecsTest.java   |  43 -
 .../org/gradle/api/artifacts/specs/TypeTest.groovy |  38 +
 .../org/gradle/api/file/FileVisitorUtil.groovy     |  26 +-
 .../AbstractMultiCauseExceptionTest.groovy         |  76 ++
 .../gradle/api/internal/XmlTransformerTest.groovy  |   2 +-
 .../CachingDependencyResolveContextTest.groovy     |   6 +-
 .../artifacts/DefaultResolverContainerTest.groovy  |  31 +-
 .../configurations/DefaultConfigurationTest.java   |  10 -
 .../dependencies/DefaultProjectDependencyTest.java |  23 +-
 .../dsl/DefaultRepositoryHandlerTest.groovy        | 129 ++-
 .../DefaultIvyDependencyResolverTest.java          |  11 +-
 .../ivyservice/DefaultResolverFactoryTest.groovy   | 125 ---
 .../ivyservice/DefaultSettingsConverterTest.groovy |  72 +-
 .../ivyservice/ErrorHandlingIvyServiceTest.groovy  |  14 +-
 .../ivyservice/LocalMavenCacheLocatorTest.groovy   |  58 --
 .../SelfResolvingDependencyResolverTest.java       |   5 +-
 .../maven/DefaultMavenPomFactoryTest.groovy        |  46 --
 .../publish/maven/DefaultMavenPomTest.groovy       | 191 -----
 .../publish/maven/DeployTaskFactoryTest.java       |  31 -
 .../DefaultPomDependenciesConverterTest.java       | 253 ------
 .../maven/deploy/AbstractMavenResolverTest.java    | 258 ------
 .../maven/deploy/BaseMavenDeployerTest.java        | 107 ---
 .../maven/deploy/BaseMavenInstallerTest.java       |  72 --
 .../deploy/DefaultArtifactPomContainerTest.groovy  |  98 ---
 .../maven/deploy/DefaultArtifactPomTest.java       | 272 ------
 .../groovy/DefaultGroovyMavenDeployerTest.groovy   | 122 ---
 .../groovy/DefaultGroovyMavenUploaderTest.groovy   | 118 ---
 .../CacheBackedFileSnapshotRepositoryTest.groovy   |  80 ++
 .../CompositeUpToDateRuleTest.groovy               |  69 ++
 .../DefaultFileSnapshotterTest.groovy              | 660 +++++++--------
 .../DefaultTaskArtifactStateRepositoryTest.java    |  83 +-
 ...BroadcastTaskArtifactStateRepositoryTest.groovy | 107 +++
 ...hortCircuitTaskArtifactStateRepositoryTest.java | 244 +++---
 .../internal/file/AbstractFileCollectionTest.java  |  49 +-
 .../api/internal/file/AbstractFileTreeTest.groovy  |  73 +-
 .../api/internal/file/BaseDirConverterTest.groovy  |   5 +-
 .../internal/file/CompositeFileCollectionTest.java | 124 ++-
 .../api/internal/file/CompositeFileTreeTest.java   |  34 +-
 .../internal/file/DefaultDirectoryWalkerTest.java  | 288 -------
 .../internal/file/DefaultFileOperationsTest.groovy |  22 +-
 .../file/DefaultSourceDirectorySetTest.groovy      | 183 +++--
 .../gradle/api/internal/file/FileSetTest.groovy    | 304 -------
 .../gradle/api/internal/file/MapFileTreeTest.java  |  75 --
 .../file/PathResolvingFileCollectionTest.java      | 351 --------
 .../internal/file/SimpleFileCollectionTest.groovy  |  29 -
 .../file/SingletonFileCollectionTest.groovy        |  41 -
 .../api/internal/file/SingletonFileTreeTest.groovy | 162 ----
 .../api/internal/file/UnionFileCollectionTest.java |   7 +-
 .../api/internal/file/UnionFileTreeTest.java       |   8 +-
 .../api/internal/file/archive/TarFileTreeTest.java |   4 +-
 .../api/internal/file/archive/ZipFileTreeTest.java |   4 +-
 ...ciesOnlyFileCollectionResolveContextTest.groovy | 197 +++++
 .../DefaultConfigurableFileCollectionTest.java     | 368 +++++++++
 .../DefaultConfigurableFileTreeTest.groovy         | 324 ++++++++
 .../DefaultFileCollectionResolveContextTest.groovy | 401 +++++++++
 .../file/collections/DirectoryFileTreeTest.java    | 375 +++++++++
 .../collections/FileCollectionAdapterTest.groovy   |  67 ++
 .../file/collections/FileTreeAdapterTest.groovy    | 147 ++++
 .../file/collections/ListBackedFileSetTest.groovy  |  41 +
 .../internal/file/collections/MapFileTreeTest.java |  75 ++
 .../collections/SimpleFileCollectionTest.groovy    |  29 +
 .../file/collections/SingletonFileTreeTest.groovy  |  46 ++
 .../api/internal/file/copy/CopySpecImplTest.groovy | 702 ++++++++--------
 .../file/copy/FileCopySpecVisitorTest.java         |  17 +-
 .../api/internal/file/copy/LineFilterTest.groovy   | 155 ++--
 .../file/copy/NormalizingCopySpecVisitorTest.java  |  73 +-
 .../file/copy/SyncCopySpecVisitorTest.java         |  50 +-
 .../api/internal/plugins/IdePluginTest.groovy      |  65 --
 .../api/internal/project/DefaultProjectTest.groovy |   5 +-
 .../project/DefaultServiceRegistryTest.java        | 594 +++++++-------
 .../api/internal/project/ProjectFactoryTest.java   |   3 +-
 .../project/TopLevelBuildServiceRegistryTest.java  |  29 +-
 .../AnnotationProcessingTaskFactoryTest.java       | 135 ++-
 .../ExecutionShortCircuitTaskExecuterTest.java     | 121 ---
 .../PostExecutionAnalysisTaskExecuterTest.groovy   | 111 ---
 .../project/taskfactory/TaskFactoryTest.java       |   7 +
 .../internal/tasks/DefaultTaskExecuterTest.java    | 281 -------
 .../internal/tasks/DefaultTaskInputsTest.groovy    | 165 +++-
 .../tasks/ExecuteAtMostOnceTaskExecuterTest.groovy |  52 --
 .../api/internal/tasks/SkipTaskExecuterTest.java   | 116 ---
 .../execution/ExecuteActionsTaskExecuterTest.java  | 282 +++++++
 .../ExecuteAtMostOnceTaskExecuterTest.groovy       |  77 ++
 .../PostExecutionAnalysisTaskExecuterTest.groovy   |  65 ++
 .../SkipEmptySourceFilesTaskExecuterTest.groovy    |  78 ++
 .../execution/SkipOnlyIfTaskExecuterTest.java      | 114 +++
 .../SkipTaskWithNoActionsExecuterTest.groovy       |  79 ++
 .../execution/SkipUpToDateTaskExecuterTest.java    | 141 ++++
 .../execution/ValidatingTaskExecuterTest.groovy    |  78 ++
 ...sistableConfigurationObjectGeneratorTest.groovy |  62 --
 ...ertiesPersistableConfigurationObjectTest.groovy |  70 --
 .../XmlPersistableConfigurationObjectTest.groovy   |  71 --
 .../internal/tasks/generator/defaultResource.xml   |   1 -
 .../gradle/api/tasks/AntBuilderAwareUtil.groovy    |  10 +
 .../groovy/org/gradle/api/tasks/CopyTest.groovy    | 167 ++--
 .../org/gradle/api/tasks/bundling/TarTest.groovy   |   1 +
 .../org/gradle/api/tasks/bundling/ZipTest.groovy   |   1 +
 .../tasks/diagnostics/ProjectReportTaskTest.groovy |   4 -
 .../gradle/api/tasks/util/PatternSetTest.groovy    |   9 +-
 .../gradle/cache/DefaultCacheRepositoryTest.java   |   2 +-
 .../TaskNameResolvingBuildExecuterTest.java        |   4 +-
 .../DefaultScriptCompilationHandlerTest.java       |  13 +-
 .../initialization/BuildProgressLoggerTest.groovy  |  23 +-
 .../DefaultExceptionAnalyserTest.java              | 175 +++-
 .../ExceptionDecoratingClassGeneratorTest.groovy   | 123 ---
 .../org/gradle/invocation/DefaultGradleTest.java   |   2 +-
 .../logging/LoggingServiceRegistryTest.groovy      |  36 +-
 .../internal/AbstractStyledTextOutputTest.groovy   |   5 +-
 .../gradle/logging/internal/AnsiConsoleTest.groovy |   4 +-
 .../ConsoleBackedProgressRendererTest.groovy       |  87 +-
 .../internal/DefaultLoggingManagerTest.java        | 162 +++-
 .../DefaultProgressLoggerFactoryTest.groovy        | 197 ++++-
 .../DefaultStandardOutputRedirectorTest.groovy     |   4 +-
 .../internal/OutputEventRendererTest.groovy        |  11 +-
 .../logging/internal/OutputSpecification.groovy    |  31 +-
 .../internal/ProgressLogEventGeneratorTest.groovy  | 139 ++--
 .../StreamBackedStandardOutputListenerTest.groovy  |  56 ++
 .../process/internal/DefaultExecHandleTest.java    |   2 +-
 .../internal/DefaultWorkerProcessFactoryTest.java  |   6 +-
 .../internal/DefaultWorkerProcessTest.groovy       |   4 +-
 .../internal/JavaExecHandleBuilderTest.groovy      |   4 +-
 .../org/gradle/util/ConcurrentSpecification.groovy |  84 +-
 .../gradle/util/ConcurrentSpecificationTest.groovy |  13 +-
 .../src/test/groovy/org/gradle/util/GUtilTest.java |  32 +-
 .../org/gradle/util/GradleVersionTest.groovy       |  77 +-
 .../gradle/util/LineBufferingOutputStreamTest.java |   2 +-
 .../src/test/groovy/org/gradle/util/Matchers.java  |  58 +-
 .../groovy/org/gradle/util/NameMatcherTest.java    |  39 +-
 .../test/groovy/org/gradle/util/StageTest.groovy   |  71 ++
 .../src/test/groovy/org/gradle/util/TestFile.java  |  25 +-
 .../groovy/org/gradle/util/TextUtilTest.groovy     |  16 +-
 .../internal/tasks/generator/defaultResource.xml   |   1 -
 subprojects/docs/docs.gradle                       |  85 +-
 subprojects/docs/src/docs/dsl/dsl.xml              |  23 +-
 ...dle.api.plugins.ApplicationPluginConvention.xml |  28 +
 ...i.plugins.antlr.AntlrSourceVirtualDirectory.xml |   4 -
 .../dsl/org.gradle.api.plugins.sonar.Sonar.xml     |  88 ++
 ...le.api.tasks.application.CreateStartScripts.xml |  23 +
 .../dsl/org.gradle.api.tasks.wrapper.Wrapper.xml   |   4 +-
 ...org.gradle.plugins.eclipse.EclipseClasspath.xml |  65 --
 .../dsl/org.gradle.plugins.eclipse.EclipseWtp.xml  |  96 ---
 ...> org.gradle.plugins.ide.api.GeneratorTask.xml} |   0
 ...rg.gradle.plugins.ide.api.XmlGeneratorTask.xml} |   0
 ...lugins.ide.eclipse.GenerateEclipseClasspath.xml |  65 ++
 ...dle.plugins.ide.eclipse.GenerateEclipseJdt.xml} |   0
 ...plugins.ide.eclipse.GenerateEclipseProject.xml} |   0
 ...ins.ide.eclipse.GenerateEclipseWtpComponent.xml |  72 ++
 ...plugins.ide.eclipse.GenerateEclipseWtpFacet.xml |  38 +
 ....gradle.plugins.ide.idea.GenerateIdeaModule.xml | 103 +++
 ...radle.plugins.ide.idea.GenerateIdeaProject.xml} |   0
 ...dle.plugins.ide.idea.GenerateIdeaWorkspace.xml} |   0
 .../dsl/org.gradle.plugins.idea.IdeaModule.xml     |  90 --
 subprojects/docs/src/docs/dsl/plugins.xml          |   3 +
 .../docs/src/docs/userguide/announcePlugin.xml     |   4 +-
 .../docs/src/docs/userguide/applicationPlugin.xml  | 105 +++
 .../docs/src/docs/userguide/buildLifecycle.xml     |   2 +-
 .../src/docs/userguide/buildScriptsTutorial.xml    |  10 +-
 .../docs/src/docs/userguide/customPlugins.xml      |  14 +-
 subprojects/docs/src/docs/userguide/depMngmt.xml   |  27 +-
 .../docs/src/docs/userguide/eclipsePlugin.xml      | 295 ++++---
 subprojects/docs/src/docs/userguide/ideaPlugin.xml | 273 +++---
 .../docs/src/docs/userguide/installation.xml       |  12 +-
 subprojects/docs/src/docs/userguide/javaPlugin.xml |   2 +-
 subprojects/docs/src/docs/userguide/logging.xml    |   4 +-
 .../docs/src/docs/userguide/mavenPlugin.xml        |   2 +-
 .../docs/src/docs/userguide/scalaPlugin.xml        |  14 +
 .../docs/src/docs/userguide/sonarPlugin.xml        |  83 ++
 .../docs/src/docs/userguide/standardPlugins.xml    |  22 +
 subprojects/docs/src/docs/userguide/userguide.xml  |   2 +
 .../docs/src/docs/userguide/workingWithFiles.xml   |   2 +-
 subprojects/docs/src/samples/antlr/build.gradle    |   2 +-
 .../docs/src/samples/application/build.gradle      |  18 +
 .../docs/src/samples/application/readme.xml        |   3 +
 .../src/main/java/org/gradle/sample/Main.java      |   9 +
 .../samples/clientModuleDependencies/build.gradle  |   2 +-
 .../docs/src/samples/codeQuality/build.gradle      |  24 +-
 .../docs/src/samples/customPlugin/build.gradle     |   2 +-
 subprojects/docs/src/samples/eclipse/build.gradle  |   6 +-
 .../samples/groovy/customizedLayout/build.gradle   |   4 +-
 .../src/samples/groovy/groovy-1.5.6/build.gradle   |   2 +-
 .../src/samples/groovy/groovy-1.6.7/build.gradle   |   2 +-
 .../samples/groovy/mixedJavaAndGroovy/build.gradle |   4 +-
 .../groovy/multiproject/testproject/build.gradle   |   4 +-
 .../src/test/groovy/org/gradle/VersionTest.groovy  |   4 +-
 .../src/samples/groovy/quickstart/build.gradle     |   4 +-
 .../src/test/groovy/org/gradle/PersonTest.groovy   |   3 +-
 .../docs/src/samples/ivypublish/build.gradle       |   4 +-
 .../docs/src/samples/java/base/test/build.gradle   |   2 +-
 .../src/samples/java/customizedLayout/build.gradle |   2 +-
 .../src/samples/java/multiproject/build.gradle     |   2 +-
 .../java/multiproject/buildSrc/build.gradle        |   2 +-
 .../docs/src/samples/java/onlyif/build.gradle      |   2 +-
 .../src/main/resources/org/gradle/resource.xml     |   1 +
 .../test/resources/org/gradle/test-resource.xml    |   1 +
 .../samples/java/withIntegrationTests/build.gradle |   2 +-
 .../src/samples/maven/pomGeneration/build.gradle   |   4 +-
 .../docs/src/samples/maven/quickstart/build.gradle |   3 +-
 subprojects/docs/src/samples/osgi/build.gradle     |   2 +-
 .../samples/scala/customizedLayout/build.gradle    |   2 +-
 .../docs/src/samples/scala/fsc/build.gradle        |  33 +
 subprojects/docs/src/samples/scala/fsc/readme.xml  |   3 +
 .../main/scala/org/gradle/sample/api/Person.scala  |   9 +
 .../scala/org/gradle/sample/impl/PersonImpl.scala  |  12 +
 .../samples/scala/mixedJavaAndScala/build.gradle   |   2 +-
 .../docs/src/samples/scala/quickstart/build.gradle |   2 +-
 subprojects/docs/src/samples/sonar/build.gradle    |  25 +
 .../sonar/src/main/java/org/gradle/Person.java     |  16 +
 .../sonar/src/test/java/org/gradle/PersonTest.java |  12 +
 .../samples/testng/java-jdk15-passing/build.gradle |   2 +-
 .../samples/testng/suitexmlbuilder/build.gradle    |   2 +-
 .../docs/src/samples/toolingApi/build.gradle       |  26 -
 .../docs/src/samples/toolingApi/build/build.gradle |  28 +
 .../docs/src/samples/toolingApi/build/readme.xml   |   3 +
 .../src/main/java/org/gradle/sample/Main.java      |  35 +
 .../docs/src/samples/toolingApi/model/build.gradle |  28 +
 .../docs/src/samples/toolingApi/model/readme.xml   |   3 +
 .../src/main/java/org/gradle/sample/Main.java      |  39 +
 .../src/main/java/org/gradle/sample/Main.java      |  32 -
 .../artifacts/defineRepository/build.gradle        |  19 +
 .../artifacts/externalDependencies/build.gradle    |  12 +-
 .../userguide/initScripts/customLogger/init.gradle |   2 +-
 .../addKrill/water/bluewhale/.ignore-me}           |   0
 .../multiproject/addKrill/water/krill/.ignore-me}  |   0
 .../addSpecifics/water/bluewhale/.ignore-me}       |   0
 .../addSpecifics/water/krill/.ignore-me}           |   0
 .../addTropical/water/tropicalFish/.ignore-me}     |   0
 .../src/main/resources/org/gradle/resource.txt}    |   0
 .../api/src/test/java/org/gradle/PersonTest.java   |   9 +
 .../test/resources/org/gradle/test-resource.txt}   |   0
 .../multiproject/dependencies/java/build.gradle    |  10 +-
 .../org/gradle/sample/services/PersonService.java  |   2 +-
 .../src/main/resources/org/gradle/resource.txt}    |   0
 .../gradle/sample/services/PersonServiceTest.java  |   2 +-
 .../test/resources/org/gradle/test-resource.txt}   |   0
 .../src/main/resources/org/gradle/resource.txt}    |   0
 .../src/test/java/org/gradle/HelperTest.java       |   9 +
 .../test/resources/org/gradle/test-resource.txt}   |   0
 .../dependencies/javaWithCustomConf/build.gradle   |   2 +-
 .../water/bluewhale/.ignore-me}                    |   0
 .../.gitignore => flat/dolphin/.ignore-me}         |   0
 .../bluewhale/.gitignore => flat/shark/.ignore-me} |   0
 .../dolphin/.ignore-me}                            |   0
 .../shark/.ignore-me}                              |   0
 .../water/bluewhale/.ignore-me}                    |   0
 .../water/krill/.ignore-me}                        |   0
 .../water/bluewhale/.ignore-me}                    |   0
 .../useSubprojects/water/krill/.gitignore          |   0
 .../water/krill/.ignore-me}                        |   0
 .../userguide/organizeBuildLogic/build.gradle      |   2 +-
 .../userguide/tutorial/antChecksum/build.gradle    |  10 -
 .../tutorial/antChecksumFiles/agile_manifesto.html |  78 --
 .../antChecksumFiles/agile_principles.html         |  89 --
 .../tutorial/antChecksumFiles/dylan_thomas.txt     |   9 -
 .../tutorial/antChecksumWithMethod/build.gradle    |  17 -
 .../userguide/tutorial/antLoadfile/build.gradle    |  10 +
 .../antLoadfileResources/agile.manifesto.txt       |   4 +
 .../antLoadfileResources/gradle.manifesto.txt      |   2 +
 .../tutorial/antLoadfileWithMethod/build.gradle    |  17 +
 .../samples/userguide/tutorial/groovy/build.gradle |   4 +-
 .../userguide/tutorial/projectReports/build.gradle |   2 +-
 .../src/samples/userguideOutput/antChecksum.out    |   3 -
 .../userguideOutput/antChecksumWithMethod.out      |   3 -
 .../src/samples/userguideOutput/antLoadfile.out    |   8 +
 .../userguideOutput/antLoadfileWithMethod.out      |   2 +
 .../userguideOutput/dependencyListReport.out       |   4 +-
 .../samples/webApplication/customised/build.gradle |   2 +-
 subprojects/eclipse/eclipse.gradle                 |  30 -
 .../gradle/plugins/eclipse/EclipseClasspath.groovy | 104 ---
 .../org/gradle/plugins/eclipse/EclipseJdt.groovy   |  49 --
 .../gradle/plugins/eclipse/EclipsePlugin.groovy    | 159 ----
 .../gradle/plugins/eclipse/EclipseProject.groovy   | 129 ---
 .../org/gradle/plugins/eclipse/EclipseWtp.groovy   | 187 -----
 .../eclipse/model/AbstractClasspathEntry.groovy    | 146 ----
 .../plugins/eclipse/model/AbstractLibrary.groovy   |  84 --
 .../gradle/plugins/eclipse/model/AccessRule.groovy |  58 --
 .../plugins/eclipse/model/BuildCommand.groovy      |  60 --
 .../gradle/plugins/eclipse/model/Classpath.groovy  | 100 ---
 .../plugins/eclipse/model/ClasspathEntry.java      |  28 -
 .../gradle/plugins/eclipse/model/Container.groovy  |  41 -
 .../org/gradle/plugins/eclipse/model/Facet.groovy  |  70 --
 .../org/gradle/plugins/eclipse/model/Jdt.java      |  71 --
 .../gradle/plugins/eclipse/model/Library.groovy    |  37 -
 .../org/gradle/plugins/eclipse/model/Link.groovy   |  65 --
 .../org/gradle/plugins/eclipse/model/Output.groovy |  65 --
 .../gradle/plugins/eclipse/model/Project.groovy    | 216 -----
 .../plugins/eclipse/model/ProjectDependency.groovy |  47 --
 .../plugins/eclipse/model/SourceFolder.groovy      |  92 ---
 .../gradle/plugins/eclipse/model/Variable.groovy   |  37 -
 .../plugins/eclipse/model/WbDependentModule.groovy |  70 --
 .../gradle/plugins/eclipse/model/WbProperty.groovy |  67 --
 .../gradle/plugins/eclipse/model/WbResource.groovy |  69 --
 .../org/gradle/plugins/eclipse/model/Wtp.groovy    | 184 -----
 .../eclipse/model/internal/ClasspathFactory.groovy | 207 -----
 .../plugins/eclipse/model/internal/PathUtil.groovy |  27 -
 .../eclipse/model/internal/WtpFactory.groovy       | 117 ---
 .../gradle/plugins/eclipse/model/package-info.java |  20 -
 .../org/gradle/plugins/eclipse/package-info.java   |  20 -
 .../META-INF/gradle-plugins/eclipse.properties     |  16 -
 .../plugins/eclipse/EclipseClasspathTest.groovy    |  54 --
 .../plugins/eclipse/EclipsePluginTest.groovy       | 180 ----
 .../plugins/eclipse/EclipseProjectTest.groovy      |  53 --
 .../gradle/plugins/eclipse/EclipseWtpTest.groovy   |  96 ---
 .../plugins/eclipse/model/ClasspathTest.groovy     | 100 ---
 .../plugins/eclipse/model/ContainerTest.groovy     |  66 --
 .../gradle/plugins/eclipse/model/FacetTest.groovy  |  58 --
 .../gradle/plugins/eclipse/model/JdtTest.groovy    |  97 ---
 .../plugins/eclipse/model/LibraryTest.groovy       |  66 --
 .../gradle/plugins/eclipse/model/OutputTest.groovy |  58 --
 .../eclipse/model/ProjectDependencyTest.groovy     |  66 --
 .../plugins/eclipse/model/ProjectTest.groovy       | 113 ---
 .../plugins/eclipse/model/SourceFolderTest.groovy  |  61 --
 .../plugins/eclipse/model/VariableTest.groovy      |  68 --
 .../eclipse/model/WbDependentModuleTest.groovy     |  61 --
 .../plugins/eclipse/model/WbPropertyTest.groovy    |  58 --
 .../plugins/eclipse/model/WbResourceTest.groovy    |  58 --
 .../gradle/plugins/eclipse/model/WtpTest.groovy    | 220 -----
 .../gradle/plugins/eclipse/model/customProject.xml |  29 -
 subprojects/ide/ide.gradle                         |  38 +
 .../plugins/ide/AbstractIdeIntegrationTest.groovy  |  66 ++
 .../plugins/ide/AutoTestedSamplesTest.groovy       |  31 +
 .../eclipse/AbstractEclipseIntegrationTest.groovy  |  73 ++
 .../eclipse/EclipseClasspathIntegrationTest.groovy |  88 ++
 .../ide/eclipse/EclipseIntegrationTest.groovy      | 270 ++++++
 .../EclipseMultiModuleIntegrationTest.groovy       | 154 ++++
 .../eclipse/EclipseProjectIntegrationTest.groovy   |  87 ++
 .../ide/eclipse/EclipseWtpIntegrationTest.groovy   | 209 +++++
 .../eclipse/EclipseWtpModelIntegrationTest.groovy  | 182 ++++
 .../plugins/ide/idea/ConfigurationHooksTest.groovy |  85 ++
 .../plugins/ide/idea/IdeaIntegrationTest.groovy    | 246 ++++++
 .../ide/idea/IdeaModuleIntegrationTest.groovy      | 172 ++++
 .../ide/idea/IdeaMultiModuleIntegrationTest.groovy | 300 +++++++
 .../ide/idea/IdeaProjectIntegrationTest.groovy     |  64 ++
 .../canCreateAndDeleteMetaData/api/build.gradle    |   0
 .../src/integTest/java/org/gradle/SomeClass.java   |   3 +
 .../src/main/java/org/gradle/api/PersonList.java   |   0
 .../api/src/main/resources/someprops.properties    |   0
 .../test/java/org/gradle/shared/PersonTest.java    |   0
 .../api/src/test}/resources/someprops.properties   |   0
 .../expectedFiles/apiClasspath.xml                 |  20 +
 .../expectedFiles/apiJdt.properties                |   0
 .../expectedFiles/apiProject.xml                   |  26 +
 .../expectedFiles/groovyprojectClasspath.xml       |   0
 .../expectedFiles/groovyprojectJdt.properties      |   0
 .../expectedFiles/groovyprojectProject.xml         |  16 +
 .../expectedFiles/javabaseprojectClasspath.xml     |   0
 .../expectedFiles/javabaseprojectJdt.properties    |   0
 .../expectedFiles/javabaseprojectProject.xml       |  15 +
 .../expectedFiles/masterProject.xml                |   8 +
 .../expectedFiles/webAppJava6Classpath.xml         |   0
 .../expectedFiles/webAppJava6Jdt.properties        |   0
 .../expectedFiles/webAppJava6Project.xml           |  26 +
 .../expectedFiles/webAppJava6WtpComponent.xml      |   7 +
 .../expectedFiles/webAppJava6WtpFacet.xml          |   0
 .../expectedFiles/webAppWithVarsClasspath.xml      |   0
 .../expectedFiles/webAppWithVarsJdt.properties     |   0
 .../expectedFiles/webAppWithVarsProject.xml        |  26 +
 .../expectedFiles/webAppWithVarsWtpComponent.xml   |  10 +
 .../expectedFiles/webAppWithVarsWtpFacet.xml       |   0
 .../expectedFiles/webserviceClasspath.xml          |   0
 .../expectedFiles/webserviceJdt.properties         |   0
 .../expectedFiles/webserviceProject.xml            |  26 +
 .../expectedFiles/webserviceWtpComponent.xml       |  13 +
 .../expectedFiles/webserviceWtpFacet.xml           |   0
 .../groovyproject/build.gradle                     |   0
 .../groovyproject/src/main/groovy/script.groovy    |   0
 .../src/main/java/org/gradle/api/PersonList.java   |   0
 .../src/main/resources/someprops.properties        |   0
 .../test/java/org/gradle/shared/PersonTest.java    |   0
 .../src/test}/resources/someprops.properties       |   0
 .../javabaseproject/build.gradle                   |   0
 .../canCreateAndDeleteMetaData/master/build.gradle | 107 +++
 .../master/settings.gradle                         |   0
 .../webAppJava6/build.gradle                       |   0
 .../src/main/java/org/gradle/Person.java           |   0
 .../webAppJava6/src/main/webapp/index.html         |   1 +
 .../webAppWithVars/build.gradle                    |   9 +
 .../src/main/java/org/gradle/Person.java           |   0
 .../webservice/build.gradle                        |   0
 .../main/java/org/gradle/webservice/TestTest.java  |   0
 .../canCreateAndDeleteMetaData/api/build.gradle    |   0
 .../src/main/java/org/gradle/api/PersonList.java   |   0
 .../test/java/org/gradle/shared/PersonTest.java    |   0
 .../canCreateAndDeleteMetaData/build.gradle        |   0
 .../expectedFiles/api/api.iml.xml                  |  36 +
 .../expectedFiles/root.iml.xml                     |  12 +
 .../expectedFiles/root.ipr.xml                     |   0
 .../expectedFiles/root.iws.xml                     |   0
 .../expectedFiles/webservice/webservice.iml.xml    |  71 ++
 .../canCreateAndDeleteMetaData/settings.gradle     |   0
 .../webservice/build.gradle                        |  15 +
 .../main/java/org/gradle/webservice/TestTest.java  |   0
 .../overwritesExistingDependencies/build.gradle    |   0
 .../expectedFiles/root.iml.xml                     |  33 +
 .../overwritesExistingDependencies/root.iml        |  20 +
 .../overwritesExistingDependencies/settings.gradle |   1 +
 .../build.gradle                                   |   4 +
 .../expectedFiles/root.ipr.xml                     |   0
 .../settings.gradle                                |   3 +
 .../worksWithAnEmptyProject/build.gradle           |   0
 .../expectedFiles/root.iml.xml                     |  12 +
 .../expectedFiles/root.ipr.xml                     |   0
 .../worksWithAnEmptyProject/settings.gradle        |   0
 .../expectedFiles/root/root.iml.xml                |  12 +
 .../expectedFiles/root/root.ipr.xml                |   0
 .../expectedFiles/top-level.iml.xml                |  12 +
 .../worksWithNonStandardLayout/root/build.gradle   |   0
 .../worksWithNonStandardLayout/settings.gradle     |   0
 .../org/gradle/plugins/ide/api/GeneratorTask.java  | 165 ++++
 .../gradle/plugins/ide/api/XmlGeneratorTask.java   |  74 ++
 .../org/gradle/plugins/ide/api/package-info.java   |  20 +
 .../plugins/ide/eclipse/EclipsePlugin.groovy       | 304 +++++++
 .../ide/eclipse/GenerateEclipseClasspath.groovy    | 156 ++++
 .../plugins/ide/eclipse/GenerateEclipseJdt.groovy  |  68 ++
 .../ide/eclipse/GenerateEclipseProject.groovy      | 184 +++++
 .../ide/eclipse/GenerateEclipseWtpComponent.groovy | 161 ++++
 .../ide/eclipse/GenerateEclipseWtpFacet.groovy     |  63 ++
 .../ide/eclipse/internal/EclipseNameDeduper.groovy |  36 +
 .../eclipse/model/AbstractClasspathEntry.groovy    | 148 ++++
 .../ide/eclipse/model/AbstractLibrary.groovy       |  87 ++
 .../plugins/ide/eclipse/model/AccessRule.groovy    |  58 ++
 .../plugins/ide/eclipse/model/BuildCommand.groovy  |  60 ++
 .../plugins/ide/eclipse/model/Classpath.groovy     | 104 +++
 .../plugins/ide/eclipse/model/ClasspathEntry.java  |  28 +
 .../plugins/ide/eclipse/model/Container.groovy     |  37 +
 .../ide/eclipse/model/EclipseClasspath.groovy      | 167 ++++
 .../ide/eclipse/model/EclipseDomainModel.groovy    |  27 +
 .../plugins/ide/eclipse/model/EclipseJdt.groovy    |  51 ++
 .../plugins/ide/eclipse/model/EclipseModel.groovy  | 116 +++
 .../ide/eclipse/model/EclipseProject.groovy        | 191 +++++
 .../plugins/ide/eclipse/model/EclipseWtp.groovy    | 103 +++
 .../ide/eclipse/model/EclipseWtpComponent.groovy   | 122 +++
 .../ide/eclipse/model/EclipseWtpFacet.groovy       |  54 ++
 .../gradle/plugins/ide/eclipse/model/Facet.groovy  |  70 ++
 .../org/gradle/plugins/ide/eclipse/model/Jdt.java  |  71 ++
 .../plugins/ide/eclipse/model/Library.groovy       |  37 +
 .../gradle/plugins/ide/eclipse/model/Link.groovy   |  65 ++
 .../gradle/plugins/ide/eclipse/model/Output.groovy |  65 ++
 .../plugins/ide/eclipse/model/Project.groovy       | 233 ++++++
 .../ide/eclipse/model/ProjectDependency.groovy     |  47 ++
 .../plugins/ide/eclipse/model/SourceFolder.groovy  |  91 ++
 .../plugins/ide/eclipse/model/Variable.groovy      |  37 +
 .../ide/eclipse/model/WbDependentModule.groovy     |  70 ++
 .../plugins/ide/eclipse/model/WbProperty.groovy    |  67 ++
 .../plugins/ide/eclipse/model/WbResource.groovy    |  69 ++
 .../plugins/ide/eclipse/model/WtpComponent.groovy  | 121 +++
 .../plugins/ide/eclipse/model/WtpFacet.groovy      |  78 ++
 .../eclipse/model/internal/ClasspathFactory.groovy | 217 +++++
 .../ide/eclipse/model/internal/PathUtil.groovy     |  27 +
 .../model/internal/ProjectDependencyBuilder.groovy |  33 +
 .../model/internal/WtpComponentFactory.groovy      | 119 +++
 .../plugins/ide/eclipse/model/package-info.java    |  20 +
 .../gradle/plugins/ide/eclipse/package-info.java   |  20 +
 .../plugins/ide/idea/GenerateIdeaModule.groovy     | 266 ++++++
 .../plugins/ide/idea/GenerateIdeaProject.groovy    |  87 ++
 .../plugins/ide/idea/GenerateIdeaWorkspace.groovy  |  34 +
 .../org/gradle/plugins/ide/idea/IdeaPlugin.groovy  | 147 ++++
 .../ide/idea/internal/IdeaNameDeduper.groovy       |  36 +
 .../gradle/plugins/ide/idea/model/Dependency.java  |  27 +
 .../gradle/plugins/ide/idea/model/IdeaModel.groovy |  40 +
 .../plugins/ide/idea/model/IdeaModule.groovy       | 310 +++++++
 .../plugins/ide/idea/model/IdeaModuleIml.groovy    |  88 ++
 .../plugins/ide/idea/model/IdeaProject.groovy      | 119 +++
 .../plugins/ide/idea/model/IdeaProjectIpr.groovy   |  43 +
 .../plugins/ide/idea/model/JarDirectory.groovy     |  66 ++
 .../org/gradle/plugins/ide/idea/model/Jdk.groovy   |  93 +++
 .../gradle/plugins/ide/idea/model/Module.groovy    | 333 ++++++++
 .../plugins/ide/idea/model/ModuleDependency.groovy |  91 ++
 .../plugins/ide/idea/model/ModuleLibrary.groovy    | 135 +++
 .../plugins/ide/idea/model/ModulePath.groovy       |  65 ++
 .../org/gradle/plugins/ide/idea/model/Path.groovy  | 149 ++++
 .../plugins/ide/idea/model/PathFactory.groovy      |  85 ++
 .../gradle/plugins/ide/idea/model/Project.groovy   | 134 +++
 .../gradle/plugins/ide/idea/model/Workspace.groovy |  41 +
 .../model/internal/IdeaDependenciesProvider.groovy | 200 +++++
 .../model/internal/ModuleDependencyBuilder.groovy  |  32 +
 .../plugins/ide/idea/model/package-info.java       |  20 +
 .../gradle/plugins/ide/internal/IdePlugin.groovy   |  67 ++
 .../internal/configurer/DeduplicationTarget.groovy |  42 +
 .../internal/configurer/ModuleNameDeduper.groovy   |  35 +
 .../ide/internal/configurer/ProjectDeduper.groovy  |  36 +
 .../AbstractPersistableConfigurationObject.groovy  |  67 ++
 ...PropertiesPersistableConfigurationObject.groovy |  45 +
 .../XmlPersistableConfigurationObject.groovy       |  57 ++
 .../internal/generator/generator/Generator.java    |  32 +
 .../generator/PersistableConfigurationObject.java  |  26 +
 .../PersistableConfigurationObjectGenerator.java   |  44 +
 .../internal/provider/BuildModelAction.java        |  48 ++
 .../internal/provider/EclipsePluginApplier.java    |  38 +
 .../tooling/internal/provider/ModelBuilder.java    | 112 +++
 .../internal/provider/ModelBuildingAdapter.java    |  41 +
 .../tooling/internal/provider/TasksFactory.java    |  36 +
 .../EclipseProjectDependenciesFactory.java         |  46 ++
 .../dependencies/ExternalDependenciesFactory.java  |  47 ++
 .../dependencies/SourceDirectoriesFactory.java     |  46 ++
 .../META-INF/gradle-plugins/eclipse.properties     |   1 +
 .../META-INF/gradle-plugins/idea.properties        |   1 +
 .../ide}/eclipse/model/defaultClasspath.xml        |   0
 .../ide}/eclipse/model/defaultJdtPrefs.properties  |   0
 .../plugins/ide}/eclipse/model/defaultProject.xml  |   0
 .../ide/eclipse/model/defaultWtpComponent.xml      |   3 +
 .../plugins/ide/eclipse/model/defaultWtpFacet.xml  |   4 +
 .../plugins/ide/idea/model/defaultModule.xml       |  12 +
 .../plugins/ide}/idea/model/defaultProject.xml     |   0
 .../plugins/ide}/idea/model/defaultWorkspace.xml   |   0
 .../org/gradle/plugins/ide/idea/package-info.java  |  20 +
 .../plugins/ide/eclipse/EclipsePluginTest.groovy   | 198 +++++
 .../eclipse/GenerateEclipseClasspathTest.groovy    |  56 ++
 .../ide/eclipse/GenerateEclipseProjectTest.groovy  |  55 ++
 .../eclipse/GenerateEclipseWtpComponentTest.groovy |  62 ++
 .../ide/eclipse/GenerateEclipseWtpFacetTest.groovy |  44 +
 .../plugins/ide/eclipse/model/ClasspathTest.groovy | 101 +++
 .../plugins/ide/eclipse/model/ContainerTest.groovy |  66 ++
 .../ide/eclipse/model/EclipseModelTest.groovy      |  49 ++
 .../ide/eclipse/model/EclipseProjectTest.groovy    |  57 ++
 .../plugins/ide/eclipse/model/FacetTest.groovy     |  58 ++
 .../plugins/ide/eclipse/model/JdtTest.groovy       |  97 +++
 .../plugins/ide/eclipse/model/LibraryTest.groovy   |  66 ++
 .../plugins/ide/eclipse/model/OutputTest.groovy    |  58 ++
 .../ide/eclipse/model/ProjectDependencyTest.groovy |  66 ++
 .../plugins/ide/eclipse/model/ProjectTest.groovy   | 111 +++
 .../ide/eclipse/model/SourceFolderTest.groovy      |  61 ++
 .../plugins/ide/eclipse/model/VariableTest.groovy  |  68 ++
 .../ide/eclipse/model/WbDependentModuleTest.groovy |  61 ++
 .../ide/eclipse/model/WbPropertyTest.groovy        |  58 ++
 .../ide/eclipse/model/WbResourceTest.groovy        |  58 ++
 .../ide/eclipse/model/WtpComponentTest.groovy      |  91 ++
 .../plugins/ide/eclipse/model/WtpFacetTest.groovy  |  80 ++
 .../internal/ProjectDependencyBuilderTest.groovy   |  59 ++
 .../ide/idea/ GenerateIdeaModuleTest.groovy        |  53 ++
 .../gradle/plugins/ide/idea/IdeaPluginTest.groovy  | 132 +++
 .../ide/idea/model/ModuleDependencyTest.groovy     |  51 ++
 .../ide/idea/model/ModuleLibraryTest.groovy        |  54 ++
 .../plugins/ide/idea/model/ModulePathTest.groovy   |  31 +
 .../plugins/ide/idea/model/ModuleTest.groovy       | 121 +++
 .../plugins/ide/idea/model/PathFactoryTest.groovy  | 170 ++++
 .../gradle/plugins/ide/idea/model/PathTest.groovy  | 113 +++
 .../plugins/ide/idea/model/ProjectTest.groovy      |  90 ++
 .../internal/ModuleDependencyBuilderTest.groovy    |  50 ++
 .../plugins/ide/internal/GeneratorTaskTest.groovy  | 111 +++
 .../plugins/ide/internal/IdePluginTest.groovy      |  65 ++
 .../configurer/DeduplicationTargetTest.groovy      |  50 ++
 .../configurer/ModuleNameDeduperTest.groovy        | 103 +++
 .../internal/configurer/ProjectDeduperTest.groovy  |  46 ++
 ...ertiesPersistableConfigurationObjectTest.groovy |  70 ++
 .../XmlPersistableConfigurationObjectTest.groovy   |  71 ++
 ...sistableConfigurationObjectGeneratorTest.groovy |  62 ++
 .../internal/provider/TasksFactoryTest.groovy      |  54 ++
 .../EclipseProjectDependenciesFactoryTest.groovy   |  50 ++
 .../ExternalDependenciesFactoryTest.groovy         |  47 ++
 .../SourceDirectoriesFactoryTest.groovy            |  48 ++
 .../plugins/ide}/eclipse/model/customClasspath.xml |   0
 .../model/customOrgEclipseWstCommonComponent.xml   |   0
 ...ustomOrgEclipseWstCommonProjectFacetCoreXml.xml |   0
 .../plugins/ide/eclipse/model/customProject.xml    |  29 +
 .../plugins/ide}/idea/model/customModule.xml       |   0
 .../plugins/ide}/idea/model/customProject.xml      |   0
 .../plugins/ide}/idea/model/customWorkspace.xml    |   0
 .../internal}/generator/defaultResource.properties |   0
 .../ide/internal/generator/defaultResource.xml     |   1 +
 subprojects/idea/idea.gradle                       |  26 -
 .../org/gradle/plugins/idea/IdeaModule.groovy      | 278 -------
 .../org/gradle/plugins/idea/IdeaPlugin.groovy      | 113 ---
 .../org/gradle/plugins/idea/IdeaProject.groovy     |  68 --
 .../org/gradle/plugins/idea/IdeaWorkspace.groovy   |  33 -
 .../org/gradle/plugins/idea/model/Dependency.java  |  27 -
 .../gradle/plugins/idea/model/JarDirectory.groovy  |  66 --
 .../org/gradle/plugins/idea/model/Jdk.groovy       |  93 ---
 .../org/gradle/plugins/idea/model/Module.groovy    | 317 -------
 .../plugins/idea/model/ModuleDependency.groovy     |  91 --
 .../gradle/plugins/idea/model/ModuleLibrary.groovy | 135 ---
 .../gradle/plugins/idea/model/ModulePath.groovy    |  65 --
 .../org/gradle/plugins/idea/model/Path.groovy      | 149 ----
 .../gradle/plugins/idea/model/PathFactory.groovy   |  84 --
 .../org/gradle/plugins/idea/model/Project.groovy   | 135 ---
 .../org/gradle/plugins/idea/model/Workspace.groovy |  41 -
 .../gradle/plugins/idea/model/package-info.java    |  20 -
 .../META-INF/gradle-plugins/idea.properties        |  16 -
 .../gradle/plugins/idea/model/defaultModule.xml    |  12 -
 .../org/gradle/plugins/idea/package-info.java      |  20 -
 .../org/gradle/plugins/idea/IdeaPluginTest.groovy  | 134 ---
 .../plugins/idea/model/ModuleDependencyTest.groovy |  51 --
 .../plugins/idea/model/ModuleLibraryTest.groovy    |  54 --
 .../plugins/idea/model/ModulePathTest.groovy       |  31 -
 .../gradle/plugins/idea/model/ModuleTest.groovy    | 118 ---
 .../plugins/idea/model/PathFactoryTest.groovy      | 170 ----
 .../org/gradle/plugins/idea/model/PathTest.groovy  | 113 ---
 .../gradle/plugins/idea/model/ProjectTest.groovy   |  90 --
 subprojects/integ-test/integ-test.gradle           |  36 +
 .../integtests/AntProjectIntegrationTest.groovy    | 158 ++++
 .../gradle/integtests/AntlrIntegrationTest.java    |  27 +
 .../integtests/ApplicationIntegrationTest.groovy   | 124 +++
 .../integtests/ArchiveIntegrationTest.groovy       | 662 +++++++++++++++
 .../ArtifactDependenciesIntegrationTest.groovy     | 274 +++++++
 .../BuildAggregationIntegrationTest.groovy         |  95 +++
 .../BuildScriptClasspathIntegrationTest.java       | 185 +++++
 .../BuildScriptErrorIntegrationTest.java           | 108 +++
 .../BuildScriptExecutionIntegrationTest.groovy     |  82 ++
 .../integtests/CacheProjectIntegrationTest.groovy  | 123 +++
 ...ntModuleDependenciesResolveIntegrationTest.java |   0
 .../integtests/CodeQualityIntegrationTest.groovy   | 188 +++++
 .../integtests/CommandLineIntegrationTest.groovy   | 142 ++++
 .../integtests/CopyErrorIntegrationTest.groovy     |  76 ++
 .../integtests/CopyTaskIntegrationTest.groovy      | 368 +++++++++
 ...CrossVersionCompatibilityIntegrationTest.groovy |  83 ++
 .../DependenciesResolveIntegrationTest.java        |   0
 .../integtests/DistributionIntegrationTest.groovy  | 147 ++++
 .../integtests/DynamicObjectIntegrationTest.groovy |   0
 .../gradle/integtests/ExecIntegrationTest.groovy   |  42 +
 .../ExternalPluginIntegrationTest.groovy           |  69 ++
 .../ExternalScriptErrorIntegrationTest.groovy      |  92 +++
 .../ExternalScriptExecutionIntegrationTest.groovy  | 186 +++++
 .../integtests/FileTreeCopyIntegrationTest.groovy  |  83 ++
 .../integtests/GroovyProjectIntegrationTest.java   |  38 +
 .../IncrementalBuildIntegrationTest.groovy         | 379 +++++++++
 .../IncrementalGroovyCompileIntegrationTest.groovy |  53 ++
 ...ementalGroovyProjectBuildIntegrationTest.groovy |  55 ++
 .../IncrementalJavaCompileIntegrationTest.groovy   |   0
 ...crementalJavaProjectBuildIntegrationTest.groovy |   0
 .../IncrementalScalaCompileIntegrationTest.groovy  |  53 ++
 .../IncrementalTestIntegrationTest.groovy          |   0
 .../integtests/InitScriptErrorIntegrationTest.java |  51 ++
 .../InitScriptExecutionIntegrationTest.groovy      |  88 ++
 .../integtests/IvyPublishIntegrationTest.groovy    | 123 +++
 .../gradle/integtests/JUnitIntegrationTest.groovy  | 404 +++++++++
 .../integtests/JUnitTestExecutionResult.groovy     | 167 ++++
 .../integtests/JavaProjectIntegrationTest.java     | 102 +++
 .../integtests/LoggingIntegrationTest.groovy       | 362 ++++++++
 .../integtests/MultiprojectIntegrationTest.groovy  |  48 ++
 .../OsgiProjectSampleIntegrationTest.groovy        |  59 ++
 .../integtests/ProjectLayoutIntegrationTest.groovy |   0
 .../integtests/ProjectLoadingIntegrationTest.java  | 254 ++++++
 .../integtests/SamplesAntlrIntegrationTest.groovy  |   0
 .../SamplesApplicationIntegrationTest.groovy       |  80 ++
 .../SamplesCodeQualityIntegrationTest.groovy       |   0
 ...amplesCustomBuildLanguageIntegrationTest.groovy |   0
 .../SamplesCustomPluginIntegrationTest.groovy      |   0
 ...lesExcludesAndClassifiersIntegrationTest.groovy |   0
 ...lesGroovyCustomizedLayoutIntegrationTest.groovy |   0
 ...SamplesGroovyMultiProjectIntegrationTest.groovy |   0
 .../SamplesGroovyOldVersionsIntegrationTest.groovy |   0
 .../SamplesGroovyQuickstartIntegrationTest.groovy  |   0
 .../SamplesIvyPublishIntegrationTest.groovy        |  41 +
 .../SamplesJavaBaseIntegrationTest.groovy          |   0
 ...mplesJavaCustomizedLayoutIntegrationTest.groovy |   0
 .../SamplesJavaMultiProjectIntegrationTest.groovy  |   0
 .../SamplesJavaOnlyIfIntegrationTest.groovy        |  92 +++
 ...esJavaProjectWithIntTestsIntegrationTest.groovy |   0
 .../SamplesJavaQuickstartIntegrationTest.groovy    |  71 ++
 ...SamplesMixedJavaAndGroovyIntegrationTest.groovy |   0
 .../SamplesMixedJavaAndScalaIntegrationTest.groovy |   0
 .../SamplesRepositoriesIntegrationTest.groovy      |   0
 ...plesScalaCustomizedLayoutIntegrationTest.groovy |   0
 .../SamplesScalaQuickstartIntegrationTest.groovy   |   0
 .../SamplesWebProjectIntegrationTest.groovy        |   0
 .../SamplesWebQuickstartIntegrationTest.groovy     |   0
 .../integtests/ScalaProjectIntegrationTest.java    |  38 +
 .../SettingsScriptErrorIntegrationTest.java        |  41 +
 .../SettingsScriptExecutionIntegrationTest.groovy  |  69 ++
 .../integtests/SyncTaskIntegrationTest.groovy      |  55 ++
 .../TaskAutoDependencyIntegrationTest.groovy       |  72 ++
 .../integtests/TaskDefinitionIntegrationTest.java  | 136 +++
 .../TaskErrorExecutionIntegrationTest.groovy       | 118 +++
 .../integtests/TaskExecutionIntegrationTest.java   | 154 ++++
 .../UserGuideSamplesIntegrationTest.groovy         |  32 +
 .../integtests/UserGuideSamplesRunner.groovy       | 274 +++++++
 .../integtests/WaterProjectIntegrationTest.groovy  |  79 ++
 .../integtests/WebProjectIntegrationTest.java      |  81 ++
 .../integtests/WorkerProcessIntegrationTest.java   |   0
 .../WrapperProjectIntegrationTest.groovy           |  56 ++
 .../maven/MavenProjectIntegrationTest.groovy       |  64 ++
 .../maven/MavenRepoIntegrationTest.groovy          |   0
 .../maven/MavenSnapshotIntegrationTest.groovy      |  95 +++
 ...SamplesMavenPomGenerationIntegrationTest.groovy | 152 ++++
 .../SamplesMavenQuickstartIntegrationTest.groovy   |  96 +++
 .../samples/CoreAutoTestedSamplesTest.groovy       |  31 +
 .../samples/PluginsAutoTestedSamplesTest.groovy    |  31 +
 .../testng/SampleTestNGIntegrationTest.groovy      |   0
 .../integtests/testng/TestNGExecutionResult.groovy | 161 ++++
 .../testng/TestNGIntegrationProject.groovy         |   0
 .../integtests/testng/TestNGIntegrationTest.groovy |   0
 .../SamplesToolingApiIntegrationTest.groovy        |  77 ++
 .../gradle/integtests/tooling/ToolingApi.groovy    | 101 +++
 .../ToolingApiBuildExecutionIntegrationTest.groovy | 152 ++++
 .../ToolingApiEclipseModelIntegrationTest.groovy   | 336 ++++++++
 ...norsProjectCustomizationsIntegrationTest.groovy | 127 +++
 .../tooling/ToolingApiIntegrationTest.groovy       |  99 +++
 .../tooling/ToolingApiModelIntegrationTest.groovy  |  63 ++
 .../tooling/ToolingApiSpecification.groovy         |  35 +
 .../projectA-1.2-ivy.xml                           |   0
 .../projectB-1.5-ivy.xml                           |   0
 .../projectWithConfigurationHierarchy.gradle       |   0
 .../projectA-1.2-ivy.xml                           |   0
 .../projectB-1.5-ivy.xml                           |   0
 .../projectWithCyclesInDependencyGraph.gradle      |   0
 .../canHaveCycleInProjectDependencies/build.gradle |   0
 .../settings.gradle                                |   0
 .../canNestModules/projectWithNestedModules.gradle |   0
 .../projectWithFlatDir.gradle                      |  28 +
 .../canUseDynamicVersions/projectA-1.2-ivy.xml     |   0
 .../canUseDynamicVersions/projectB-1.5-ivy.xml     |   0
 .../projectWithDynamicVersions.gradle              |   0
 .../projectA-1.2-ivy.xml                           |   0
 .../projectA-2.0-ivy.xml                           |   0
 .../projectB-1.5-ivy.xml                           |   0
 .../projectB-2.1.5-ivy.xml                         |   0
 .../projectWithConflicts.gradle                    |   0
 .../dependencyReportWithConflicts/settings.gradle  |   0
 .../projectWithUnknownDependency.gradle            |   0
 .../CommandLineIntegrationTest/shared/build.gradle |  20 +
 .../shared/settings.gradle                         |   0
 .../canBuildJavaProject/build.gradle               |   0
 .../src/main/groovy/org/gradle/CustomTask.groovy   |   0
 .../src/main/java/org/gradle/Person.java           |   0
 .../shared/build.gradle                            |   0
 .../canExecuteCommands/canExecuteCommands.gradle   |   0
 .../canExecuteJava/canExecuteJava.gradle           |   0
 .../shared/src/main/java/org/gradle/TestMain.java  |   0
 .../buildSrc/src/main/java/DirTransformerTask.java |   0
 .../buildSrc/src/main/java/GeneratorTask.java      |   0
 .../buildSrc/src/main/java/TransformerTask.java    |   0
 .../recompilesDependentClasses/NewIPerson.groovy   |   0
 .../recompilesDependentClasses/build.gradle        |   0
 .../src/main/groovy/IPerson.groovy                 |   0
 .../src/main/groovy/Person.groovy                  |   0
 .../build.gradle                                   |   0
 .../src/main/groovy/Person.java                    |   0
 .../src/main/groovy/PersonImpl.Groovy              |   0
 .../recompilesDependentClasses/NewIPerson.java     |   0
 .../recompilesDependentClasses/build.gradle        |   0
 .../src/main/java/IPerson.java                     |   0
 .../src/main/java/Person.java                      |   0
 .../NewIPerson.java                                |   0
 .../app/src/main/java/Person.java                  |   0
 .../build.gradle                                   |   0
 .../lib/src/main/java/IPerson.java                 |   0
 .../settings.gradle                                |   0
 .../build.gradle                                   |   0
 .../src/main/java/Test.java                        |   0
 .../recompilesDependentClasses/NewIPerson.scala    |   0
 .../recompilesDependentClasses/build.gradle        |   0
 .../src/main/scala/IPerson.scala                   |   0
 .../src/main/scala/Person.scala                    |   0
 .../build.gradle                                   |   0
 .../src/main/scala/Person.java                     |   0
 .../src/main/scala/PersonImpl.scala                |   0
 .../doesNotRunStaleTests/src/test/java/Broken.java |   0
 .../build.gradle                                   |  14 +
 .../src/test/java/JUnitExtra.java                  |   0
 .../src/test/java/JUnitTest.java                   |   0
 .../src/test/java/TestNGTest.java                  |   0
 .../NewMainClass.java                              |   0
 .../executesTestsWhenSourceChanges/NewOk.java      |   0
 .../src/main/java/MainClass.java                   |   0
 .../shared/build.gradle                            |   9 +
 .../shared/src/test/java/Ok.java                   |   0
 .../canHaveMultipleTestTaskInstances/build.gradle  |  21 +
 .../src/test/java/org/gradle/Test1.java            |   0
 .../src/test/java/org/gradle/Test2.java            |   0
 .../canRunSingleTests/build.gradle                 |   9 +
 .../canRunSingleTests/src/test/java/NotATest.java  |   0
 .../canRunSingleTests/src/test/java/Ok.java        |   0
 .../canRunSingleTests/src/test/java/Ok2.java       |   0
 .../detectsTestClasses/build.gradle                |   8 +
 .../test/java/org/gradle/AbstractHasRunWith.java   |   0
 .../src/test/java/org/gradle/CustomRunner.java     |   0
 .../test/java/org/gradle/EmptyRunWithSubclass.java |   0
 .../src/test/java/org/gradle/TestsOnInner.java     |   0
 .../executesTestsInCorrectEnvironment/build.gradle |   8 +
 .../src/test/java/org/gradle/OkTest.java           |  72 ++
 .../src/test/java/org/gradle/OtherTest.java        |   0
 .../JUnitIntegrationTest/junit3Tests/build.gradle  |   9 +
 .../src/test/java/org/gradle/Junit3Test.java       |   9 +
 .../JUnitIntegrationTest/junit4Tests/build.gradle  |   9 +
 .../src/test/java/org/gradle/IgnoredTest.java      |  12 +
 .../src/test/java/org/gradle/Junit4Test.java       |  15 +
 .../junit4_4Tests/build.gradle                     |   9 +
 .../build.gradle                                   |   3 +
 .../src/test/java/org/gradle/BrokenAfter.java      |   0
 .../src/test/java/org/gradle/BrokenAfterClass.java |   0
 .../src/test/java/org/gradle/BrokenBefore.java     |   0
 .../test/java/org/gradle/BrokenBeforeAndAfter.java |   0
 .../test/java/org/gradle/BrokenBeforeClass.java    |   0
 .../test/java/org/gradle/BrokenConstructor.java    |   0
 .../src/test/java/org/gradle/BrokenException.java  |   0
 .../src/test/java/org/gradle/BrokenTest.java       |   0
 .../src/test/java/org/gradle/Unloadable.java       |   0
 .../LoggingIntegrationTest/logging/build.gradle    |   0
 .../logging/buildSrc/build.gradle                  |  22 +
 .../LoggingIntegrationTest/logging/external.gradle |   9 +
 .../LoggingIntegrationTest/logging/init.gradle     |  46 ++
 .../logging/nestedBuild/build.gradle               |   0
 .../logging/nestedBuild/buildSrc/build.gradle      |  22 +
 .../logging/nestedBuild/settings.gradle}           |   0
 .../logging/project1/build.gradle                  |   0
 .../logging/project2/build.gradle                  |   0
 .../LoggingIntegrationTest/logging/settings.gradle |   0
 .../multiThreaded/build.gradle                     |   0
 .../canUseANonStandardBuildDir/build.gradle        |  13 +
 .../src/main/java/Person.java                      |   0
 .../src/test/java/PersonTest.java                  |   0
 .../copyTestResources/src/one/ignore/bad.file      |   0
 .../integtests/copyTestResources/src/one/one.a     |   0
 .../integtests/copyTestResources/src/one/one.b     |   0
 .../copyTestResources/src/one/sub/ignore/bad.file  |   0
 .../copyTestResources/src/one/sub/onesub.a         |   0
 .../copyTestResources/src/one/sub/onesub.b         |   0
 .../gradle/integtests/copyTestResources/src/root.a |   0
 .../gradle/integtests/copyTestResources/src/root.b |   0
 .../copyTestResources/src/two/ignore/bad.file      |   0
 .../integtests/copyTestResources/src/two/two.a     |   3 +
 .../integtests/copyTestResources/src/two/two.b     |   0
 .../copyTestResources/src2/three/three.a           |   0
 .../copyTestResources/src2/three/three.b           |   0
 .../groovy/expectedClasspathFile.txt               |   0
 .../eclipseproject/groovy/expectedProjectFile.txt  |   0
 .../java/expectedApiClasspathFile.txt              |   0
 .../eclipseproject/java/expectedApiProjectFile.txt |   0
 .../java/expectedWebserviceClasspathFile.txt       |   0
 .../java/expectedWebserviceProjectFile.txt         |   0
 .../java/expectedWebserviceWtpFile.txt             |   0
 .../eclipseproject/scala/expectedClasspathFile.txt |   0
 .../eclipseproject/scala/expectedProjectFile.txt   |   0
 .../build.gradle                                   |   0
 .../settings.gradle                                |   0
 .../build.gradle                                   |   0
 .../settings.gradle                                |   0
 .../build.gradle                                   |   0
 .../settings.gradle                                |   0
 .../shared/producer.gradle                         |   0
 .../shared/projectWithMavenSnapshots.gradle        |   0
 .../shared/src/main/java/org/gradle/Test.java      |   0
 .../maven/pomGeneration/expectedNewPom.txt         |   0
 .../integtests/maven/pomGeneration/expectedPom.txt |   0
 .../maven/pomGeneration/expectedQuickstartPom.txt  |   0
 .../canListenForTestResults/build.gradle           |  20 +
 .../src/test/java/AppException.java                |   0
 .../src/test/java/SomeTest.java                    |   0
 .../executesTestsInCorrectEnvironment/build.gradle |   9 +
 .../src/test/java/org/gradle/OkTest.java           |   0
 .../groovyJdk15Failing/build.gradle                |  16 +
 .../src/main/groovy/org/gradle/Ok.groovy           |   0
 .../src/test/groovy/org/gradle/BadTest.groovy      |   0
 .../groovyJdk15Passing/build.gradle                |  16 +
 .../src/main/groovy/org/gradle/Ok.groovy           |   0
 .../src/test/groovy/org/gradle/OkTest.groovy       |   0
 .../javaJdk14Failing/build.gradle                  |   0
 .../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 |   0
 .../test/java/org/gradle/TestWithBrokenSetup.java  |   0
 .../META-INF/gradle-plugins/jetty.properties       |  17 +-
 subprojects/launcher/launcher.gradle               |  16 +-
 .../org/gradle/launcher/BuildActionParameters.java |  30 +
 .../gradle/launcher/CommandLineActionFactory.java  |  24 +-
 .../org/gradle/launcher/DaemonBuildAction.java     |  29 +-
 .../java/org/gradle/launcher/DaemonClient.java     | 102 +++
 .../org/gradle/launcher/DaemonClientAction.java    |  71 --
 .../java/org/gradle/launcher/DaemonConnector.java  |   6 +-
 .../main/java/org/gradle/launcher/DaemonMain.java  | 105 +--
 .../launcher/DefaultBuildActionParameters.java     |  50 ++
 .../DefaultGradleLauncherActionExecuter.java       |  65 ++
 .../gradle/launcher/ExceptionReportingAction.java  |  40 +
 .../org/gradle/launcher/ExecuteBuildAction.java    |  50 ++
 .../launcher/GradleLauncherActionExecuter.java     |  29 +
 .../org/gradle/launcher/InitializationAware.java   |  22 +
 .../org/gradle/launcher/ReportedException.java     |  25 +
 .../java/org/gradle/launcher/StopDaemonAction.java |  30 +-
 .../java/org/gradle/launcher/protocol/Build.java   |  32 +-
 .../gradle/launcher/protocol/CommandComplete.java  |   6 +-
 .../java/org/gradle/launcher/protocol/Result.java  |  29 +
 .../internal/provider/ConfiguringBuildAction.java  |  62 ++
 .../DaemonGradleLauncherActionExecuter.java        |  39 +
 .../internal/provider/DefaultConnection.java       |  92 +++
 .../provider/DelegatingBuildModelAction.java       |  54 ++
 .../EmbeddedGradleLauncherActionExecuter.java      |  51 ++
 .../internal/provider/ExecuteBuildAction.java      |  45 +
 ...oggingBridgingGradleLauncherActionExecuter.java |  78 ++
 ...le.tooling.internal.protocol.ConnectionVersion4 |   1 +
 .../launcher/CommandLineActionFactoryTest.groovy   |  33 +-
 .../gradle/launcher/DaemonBuildActionTest.groovy   |  30 +-
 .../org/gradle/launcher/DaemonClientTest.groovy    | 103 +++
 .../launcher/ExceptionReportingActionTest.groovy   |  62 ++
 .../gradle/launcher/StopDaemonActionTest.groovy    |  46 +-
 .../DaemonGradleLauncherActionExecuterTest.groovy  |  42 +
 ...EmbeddedGradleLauncherActionExecuterTest.groovy |  78 ++
 .../provider/ExecuteBuildActionTest.groovy         |  49 ++
 ...BridgingGradleLauncherActionExecuterTest.groovy |  59 ++
 .../gradle/api/plugins/MavenPluginConvention.java  |  23 +-
 .../META-INF/gradle-plugins/maven.properties       |  17 +-
 .../api/plugins/MavenPluginConventionTest.groovy   |  10 +-
 subprojects/open-api/open-api.gradle               |  10 -
 ...CrossVersionCompatibilityIntegrationTest.groovy |   9 +-
 subprojects/osgi/osgi.gradle                       |   5 +-
 .../internal/plugins/osgi/DefaultOsgiManifest.java |  23 +-
 .../api/internal/plugins/osgi/OsgiHelper.java      | 199 ++---
 .../org/gradle/api/plugins/osgi/OsgiManifest.java  |   7 +-
 .../org/gradle/api/plugins/osgi/OsgiPlugin.groovy  |  17 +-
 .../META-INF/gradle-plugins/osgi.properties        |  17 +-
 .../plugins/osgi/DefaultOsgiManifestTest.java      |   7 +-
 .../internal/plugins/osgi/OsgiHelperTest.groovy    |  71 ++
 subprojects/plugins/plugins.gradle                 |  40 +-
 .../api/internal/tasks/DefaultGroovySourceSet.java |  22 +-
 .../api/internal/tasks/DefaultSourceSet.java       |  31 +-
 .../tasks/compile/IncrementalJavaCompiler.java     |   4 +-
 .../testing/detection/DefaultTestExecuter.java     |   4 +-
 .../tasks/testing/junit/AntJUnitReport.groovy      |  34 -
 .../junit/JUnit4TestResultProcessorAdapter.java    |  58 --
 .../testing/junit/JUnitTestClassExecuter.java      |  62 +-
 .../testing/junit/JUnitTestClassProcessor.java     | 136 +--
 .../tasks/testing/junit/JUnitTestFramework.java    |  18 +-
 .../junit/JUnitTestResultProcessorAdapter.java     | 245 +++---
 .../testing/junit/JUnitXmlReportGenerator.java     |  37 +-
 .../tasks/testing/junit/report/AllTestResults.java |  66 ++
 .../testing/junit/report/ClassPageRenderer.java    |  91 ++
 .../testing/junit/report/ClassTestResults.java     |  86 ++
 .../testing/junit/report/CompositeTestResults.java |  90 ++
 .../testing/junit/report/DefaultTestReport.java    | 174 ++++
 .../junit/report/LocaleSafeDecimalFormat.java      |  42 +
 .../testing/junit/report/OverviewPageRenderer.java |  87 ++
 .../testing/junit/report/PackagePageRenderer.java  |  60 ++
 .../testing/junit/report/PackageTestResults.java   |  61 ++
 .../tasks/testing/junit/report/PageRenderer.java   | 198 +++++
 .../tasks/testing/junit/report/TestFailure.java    |  34 +
 .../tasks/testing/junit/report/TestReporter.java   |  26 +
 .../tasks/testing/junit/report/TestResult.java     |  97 +++
 .../testing/junit/report/TestResultModel.java      |  91 ++
 .../processors/MaxNParallelTestClassProcessor.java |   4 +-
 .../RestartEveryNTestClassProcessor.java           | 132 +--
 .../internal/tasks/testing/results/TestLogger.java |   4 +-
 .../testing/worker/ForkingTestClassProcessor.java  | 160 ++--
 .../gradle/api/plugins/ApplicationPlugin.groovy    | 126 +++
 .../api/plugins/ApplicationPluginConvention.groovy |  33 +
 .../org/gradle/api/plugins/BasePlugin.groovy       |   6 +-
 .../org/gradle/api/plugins/GroovyBasePlugin.java   |   4 +-
 .../org/gradle/api/plugins/GroovyPlugin.java       |  24 +-
 .../groovy/org/gradle/api/plugins/JavaPlugin.java  |   2 +-
 .../org/gradle/api/tasks/GroovySourceSet.java      |   3 +-
 .../groovy/org/gradle/api/tasks/SourceSet.java     |   5 +-
 .../tasks/application/CreateStartScripts.groovy    | 141 ++++
 .../org/gradle/api/tasks/bundling/Jar.groovy       |   5 +-
 .../org/gradle/api/tasks/compile/Compile.java      |   4 +-
 .../gradle/api/tasks/compile/CompileOptions.groovy |   2 +-
 .../gradle/api/tasks/javadoc/AntGroovydoc.groovy   |  28 +-
 .../org/gradle/api/tasks/javadoc/Groovydoc.java    |  40 +-
 .../org/gradle/api/tasks/javadoc/Javadoc.java      |  37 +-
 .../groovy/org/gradle/api/tasks/testing/Test.java  |   1 -
 .../META-INF/gradle-plugins/application.properties |   1 +
 .../META-INF/gradle-plugins/base.properties        |  17 +-
 .../META-INF/gradle-plugins/groovy-base.properties |  17 +-
 .../META-INF/gradle-plugins/groovy.properties      |  17 +-
 .../META-INF/gradle-plugins/java-base.properties   |  17 +-
 .../META-INF/gradle-plugins/java.properties        |  17 +-
 .../gradle-plugins/project-report.properties       |  17 +-
 .../gradle-plugins/project-reports.properties      |  17 +-
 .../META-INF/gradle-plugins/war.properties         |  17 +-
 .../internal/tasks/testing/junit/report/report.js  | 101 +++
 .../internal/tasks/testing/junit/report/style.css  | 212 +++++
 .../api/tasks/application/unixStartScript.txt      | 179 ++++
 .../api/tasks/application/windowsStartScript.txt   |  82 ++
 .../tasks/DefaultGroovySourceSetTest.groovy        |  17 +-
 .../api/internal/tasks/DefaultSourceSetTest.groovy |  22 +-
 .../junit/JUnitTestClassProcessorTest.groovy       | 195 ++++-
 .../testing/junit/JUnitTestFrameworkTest.java      |  23 +-
 .../testing/junit/report/AllTestResultsTest.groovy |  44 +
 .../junit/report/ClassTestResultsTest.groovy       |  26 +
 .../junit/report/CompositeTestResultsTest.groovy   |  68 ++
 .../junit/report/DefaultTestReportTest.groovy      | 376 +++++++++
 .../junit/report/LocaleSafeDecimalFormatTest.java  |  50 ++
 .../junit/report/TestResultModelTest.groovy        |  40 +
 .../testing/junit/report/TestResultTest.groovy     |  50 ++
 .../tasks/testing/results/TestLoggerTest.groovy    |   9 +-
 .../api/plugins/ApplicationPluginTest.groovy       | 121 +++
 .../org/gradle/api/plugins/BasePluginTest.groovy   |  12 +
 .../org/gradle/api/plugins/JavaPluginTest.groovy   |   4 +-
 .../application/CreateStartScriptsTest.groovy      |  74 ++
 .../api/tasks/compile/AbstractCompileTest.java     |   2 +-
 .../gradle/api/tasks/javadoc/GroovydocTest.java    |  14 +-
 .../org/gradle/api/tasks/javadoc/JavadocTest.java  |   2 +-
 .../org/gradle/api/tasks/testing/TestTest.java     |  40 +-
 .../api/internal/tasks/DefaultScalaSourceSet.java  |  18 +-
 .../api/plugins/scala/ScalaBasePlugin.groovy       |   4 +-
 .../org/gradle/api/tasks/ScalaSourceSet.java       |   3 +-
 .../org/gradle/api/tasks/scala/ScalaCompile.java   |   3 +-
 .../META-INF/gradle-plugins/scala-base.properties  |  17 +-
 .../META-INF/gradle-plugins/scala.properties       |  17 +-
 .../tasks/DefaultScalaSourceSetTest.groovy         |  18 +-
 .../api/plugins/scala/ScalaPluginTest.groovy       |   7 +-
 subprojects/sonar/sonar.gradle                     |  50 ++
 .../org/gradle/api/plugins/sonar/Sonar.groovy      | 258 ++++++
 .../gradle/api/plugins/sonar/SonarPlugin.groovy    |  70 ++
 .../sonar/internal/ClassesOnlyClassLoader.java     |  39 +
 .../sonar/internal/SonarCodeAnalyzer.groovy        |  54 ++
 .../META-INF/gradle-plugins/sonar.properties       |   1 +
 .../api/plugins/sonar/SonarPluginTest.groovy       |  75 ++
 .../java/org/gradle/tooling/BuildConnection.java   |  49 --
 .../java/org/gradle/tooling/BuildException.java    |  25 +
 .../java/org/gradle/tooling/BuildLauncher.java     | 107 +++
 .../gradle/tooling/GradleConnectionException.java  |   2 +-
 .../java/org/gradle/tooling/GradleConnector.java   |  77 +-
 .../main/java/org/gradle/tooling/ModelBuilder.java |  86 ++
 .../java/org/gradle/tooling/ProgressEvent.java     |  28 +
 .../java/org/gradle/tooling/ProgressListener.java  |  28 +
 .../java/org/gradle/tooling/ProjectConnection.java |  75 ++
 .../tooling/internal/DefaultEclipseProject.java    | 117 +++
 .../internal/DefaultEclipseProjectDependency.java  |  44 +
 .../internal/DefaultEclipseSourceDirectory.java    |  44 +
 .../internal/DefaultExternalDependency.java        |  45 +
 .../org/gradle/tooling/internal/DefaultTask.java   |  56 ++
 .../consumer/AbstractLongRunningOperation.java     |  99 +++
 .../tooling/internal/consumer/AsyncConnection.java |  28 +
 .../internal/consumer/BlockingResultHandler.java   |  58 ++
 .../CachingToolingImplementationLoader.java        |  21 +-
 .../internal/consumer/ConnectionFactory.java       |  42 +-
 .../internal/consumer/ConnectionParameters.java    |  39 +
 .../consumer/ConnectorServiceRegistry.java         |  47 ++
 .../internal/consumer/DefaultAsyncConnection.java  |  90 ++
 .../internal/consumer/DefaultBuildConnection.java  |  91 --
 .../internal/consumer/DefaultBuildLauncher.java    |  95 +++
 .../consumer/DefaultConnectionParameters.java      |  61 ++
 .../internal/consumer/DefaultGradleConnector.java  | 104 +++
 .../internal/consumer/DefaultModelBuilder.java     |  91 ++
 .../consumer/DefaultProjectConnection.java         |  78 ++
 .../DefaultToolingImplementationLoader.java        |  44 +-
 .../tooling/internal/consumer/Distribution.java    |   2 +
 .../internal/consumer/DistributionFactory.java     | 178 +++-
 .../tooling/internal/consumer/LazyConnection.java  | 135 +++
 .../internal/consumer/ProgressListenerAdapter.java |  51 ++
 .../consumer/ProgressLoggingConnection.java        | 105 +++
 .../internal/consumer/ProtocolToModelAdapter.java  |  20 +-
 .../internal/consumer/ResultHandlerAdapter.java    |  51 ++
 .../consumer/ToolingImplementationLoader.java      |   4 +-
 .../internal/protocol/BuildExceptionVersion1.java  |  27 +
 .../protocol/BuildOperationParametersVersion1.java |  53 ++
 .../internal/protocol/BuildParametersVersion1.java |  25 +
 .../tooling/internal/protocol/BuildVersion1.java   |  23 -
 .../protocol/BuildableProjectVersion1.java         |  23 +
 .../protocol/ConnectionFactoryVersion1.java        |  30 -
 .../protocol/ConnectionMetaDataVersion1.java       |  28 +
 .../internal/protocol/ConnectionVersion1.java      |  38 -
 .../internal/protocol/ConnectionVersion4.java      |  52 ++
 .../protocol/ExternalDependencyVersion1.java       |   5 +
 .../protocol/HierarchicalProjectVersion1.java      |  25 +
 .../LongRunningOperationParametersVersion1.java    |  44 +
 .../protocol/ProgressListenerVersion1.java         |  25 +
 .../protocol/ProjectDependencyVersion1.java        |  22 +
 .../tooling/internal/protocol/ProjectVersion1.java |  25 -
 .../tooling/internal/protocol/ProjectVersion3.java |  31 +
 .../tooling/internal/protocol/TaskVersion1.java    |  29 +
 .../protocol/eclipse/EclipseBuildVersion1.java     |  25 -
 .../eclipse/EclipseProjectDependencyVersion2.java  |  27 +
 .../protocol/eclipse/EclipseProjectVersion1.java   |  28 -
 .../protocol/eclipse/EclipseProjectVersion3.java   |  32 +
 .../eclipse/EclipseSourceDirectoryVersion1.java    |  27 +
 .../protocol/eclipse/EclipseTaskVersion1.java      |  25 +
 .../HierarchicalEclipseProjectVersion1.java        |  31 +
 .../internal/provider/DefaultConnection.java       | 118 ---
 .../provider/DefaultConnectionFactory.java         |  40 -
 .../internal/provider/DefaultEclipseBuild.java     |  31 -
 .../internal/provider/DefaultEclipseProject.java   |  46 --
 .../main/java/org/gradle/tooling/model/Build.java  |  28 -
 .../org/gradle/tooling/model/BuildableProject.java |  28 +
 .../gradle/tooling/model/ExternalDependency.java   |  14 +
 .../gradle/tooling/model/HierarchicalProject.java  |  35 +
 .../java/org/gradle/tooling/model/Project.java     |  26 +-
 .../gradle/tooling/model/ProjectDependency.java    |  28 +
 .../org/gradle/tooling/model/SourceDirectory.java  |  30 +
 .../main/java/org/gradle/tooling/model/Task.java   |  47 ++
 .../gradle/tooling/model/eclipse/EclipseBuild.java |  28 -
 .../tooling/model/eclipse/EclipseProject.java      |  24 +-
 .../model/eclipse/EclipseProjectDependency.java    |  33 +
 .../model/eclipse/EclipseSourceDirectory.java      |  30 +
 .../gradle/tooling/model/eclipse/EclipseTask.java  |  28 +
 .../model/eclipse/HierarchicalEclipseProject.java  |  48 ++
 ...ternal.protocol.GradleConnectionFactoryVersion1 |   1 -
 .../org/gradle/tooling/GradleConnectorTest.groovy  |  96 ---
 .../internal/DefaultEclipseProjectTest.groovy      |  27 +
 .../CachingToolingImplementationLoaderTest.groovy  |  28 +-
 .../internal/consumer/ConnectionFactoryTest.groovy |  39 +-
 .../consumer/DefaultBuildConnectionTest.groovy     | 143 ----
 .../consumer/DefaultBuildLauncherTest.groovy       | 145 ++++
 .../consumer/DefaultGradleConnectorTest.groovy     | 100 +++
 .../consumer/DefaultModelBuilderTest.groovy        | 137 ++++
 .../consumer/DefaultProjectConnectionTest.groovy   |  58 ++
 .../DefaultToolingImplementationLoaderTest.groovy  |  62 +-
 .../consumer/DistributionFactoryTest.groovy        | 178 ++++
 .../internal/consumer/LazyConnectionTest.groovy    | 101 +++
 .../consumer/ProgressListenerAdapterTest.groovy    |  63 ++
 .../consumer/ProgressLoggingConnectionTest.groovy  |  70 ++
 .../consumer/ProtocolToModelAdapterTest.groovy     |  32 +-
 .../tooling/internal/consumer/TestConnection.java  |  36 +
 subprojects/tooling-api/tooling-api.gradle         |   2 +-
 .../integtests/LiveOutputIntegrationTest.groovy    |  15 +-
 ...projectProjectAndTaskListIntegrationTest.groovy |  33 +-
 .../gradle/ExecuteGradleCommandClientProtocol.java |   5 +-
 .../ipc/gradle/TaskListClientProtocol.java         |   3 +-
 .../foundation/GradleInterfaceWrapperVersion1.java |   2 +-
 .../groovy/org/gradle/foundation/TestUtility.java  |  11 +-
 subprojects/ui/ui.gradle                           |   9 -
 .../java/org/gradle/api/tasks/wrapper/Wrapper.java |  11 +-
 .../wrapper/internal/DistributionLocator.java      |  48 --
 .../src/main/java/org/gradle/wrapper/Install.java  |   6 +-
 .../src/main/java/org/gradle/wrapper/Wrapper.java  |  73 +-
 .../org/gradle/api/tasks/wrapper/WrapperTest.java  |  16 +-
 .../groovy/org/gradle/wrapper/WrapperTest.groovy   | 106 +++
 .../groovy/org/gradle/wrapper/WrapperTest.java     |  77 --
 subprojects/wrapper/wrapper.gradle                 |   4 +-
 1622 files changed, 54989 insertions(+), 32936 deletions(-)

diff --git a/build.gradle b/build.gradle
index a93198e..a2913e3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -14,14 +14,12 @@
  * limitations under the License.
  */
 
-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
+import java.util.jar.Attributes
+import org.gradle.api.internal.artifacts.dependencies.DefaultSelfResolvingDependency
 
 /**
  * For building Gradle you usually don't need to specify any properties. Only certain functionality of the Gradle requires
@@ -44,28 +42,27 @@ startScriptsDir = new File("$buildDir/startScripts")
 archivesBaseName = 'gradle'
 
 libraries = [
-        ant: 'org.apache.ant:ant:1.8.1 at jar',
-        ant_junit: 'org.apache.ant:ant-junit:1.8.1 at jar',
-        ant_launcher: 'org.apache.ant:ant-launcher:1.8.1 at jar',
-        ant_nodeps: 'org.apache.ant:ant-nodeps:1.8.1 at jar',
-        ant_antlr: 'org.apache.ant:ant-antlr:1.8.1 at jar',
+        ant: 'org.apache.ant:ant:1.8.2 at jar',
+        ant_junit: 'org.apache.ant:ant-junit:1.8.2 at jar',
+        ant_launcher: 'org.apache.ant:ant-launcher:1.8.2 at jar',
+        ant_antlr: 'org.apache.ant:ant-antlr:1.8.2 at jar',
         antlr: 'antlr:antlr:2.7.7 at jar',
-        asm_all: 'asm:asm-all:3.2 at jar',
+        asm_all: 'asm:asm-all:3.3.1 at jar',
         commons_cli: 'commons-cli:commons-cli:1.2 at jar',
         commons_io: 'commons-io:commons-io:1.4 at jar',
-        commons_lang: 'commons-lang:commons-lang:2.5 at jar',
+        commons_lang: 'commons-lang:commons-lang:2.6 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.6 at jar',
+        guava: 'com.google.guava:guava:r08 at jar',
+        groovy: 'org.codehaus.groovy:groovy-all:1.7.10 at jar',
         ivy: 'org.apache.ivy:ivy:2.2.0 at jar',
         jaxen: 'jaxen:jaxen:1.1 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',
         log4j_to_slf4j: 'org.slf4j:log4j-over-slf4j:1.6.1 at jar',
-        logback_classic: 'ch.qos.logback:logback-classic:0.9.24 at jar',
-        logback_core: 'ch.qos.logback:logback-core:0.9.24 at jar',
-        junit: 'junit:junit:4.8.1',
+        logback_classic: 'ch.qos.logback:logback-classic:0.9.28 at jar',
+        logback_core: 'ch.qos.logback:logback-core:0.9.28 at jar',
+        junit: 'junit:junit:4.8.2',
         xmlunit: 'xmlunit:xmlunit:1.3',
 ]
 
@@ -95,7 +92,7 @@ allprojects {
     }
 
     repositories {
-        mavenRepo(urls: 'http://gradle.artifactoryonline.com/gradle/libs')
+        mavenRepo(urls: 'http://repo.gradle.org/gradle/libs')
     }
 
     version = this.version
@@ -103,7 +100,6 @@ allprojects {
 
 configure(groovyProjects()) {
     apply plugin: 'groovy'
-    apply plugin: 'eclipse'
 
     archivesBaseName = "gradle-${name.replaceAll("\\p{Upper}") { "-${it.toLowerCase()}" } }"
     dependencies {
@@ -144,14 +140,6 @@ allprojects {
         into "$ideDir/lib"
         from { config.copyRecursive {dep -> !(dep instanceof ProjectDependency)}.files }
     }
-    ideaModule {
-        gradleCacheVariable = 'GRADLE_CACHE'
-        outputDir = "$rootProject.projectDir/intellij/out" as File
-        testOutputDir = "$rootProject.projectDir/intellij/testOut" as File
-    }
-    eclipseClasspath {
-        variables = [:]
-    }
 }
 
 ideaModule {
@@ -293,13 +281,42 @@ ideaWorkspace {
     withXml { provider ->
         Node node = provider.asNode()
 
-        Node runConfig = node.component.find { it.'@name' == 'RunManager' }.configuration.find { it.'@type' == 'JUnit'}
+        Node runManagerConfig = node.component.find { it.'@name' == 'RunManager' }
 
-        Node module = runConfig.module[0]
-        module.'@name' = 'launcher'
+        // Add int test configuration to JUnit defaults
+        Node runConfig = runManagerConfig.configuration.find { it.'@type' == 'JUnit'}
 
         Node vmParameters = runConfig.option.find { it.'@name' == 'VM_PARAMETERS' }
         vmParameters.'@value' = "\"-DintegTest.gradleHomeDir=${intTestImage.destinationDir}\" -ea -Dorg.gradle.integtest.executer=embedded"
+
+        // Add an application configuration
+        runManagerConfig.'@selected' = 'Application.Gradle'
+        def appConfig = new XmlParser().parseText('''
+    <configuration default="false" name="Gradle" type="Application" factoryName="Application">
+      <extension name="coverage" enabled="false" merge="false" />
+      <option name="MAIN_CLASS_NAME" value="org.gradle.launcher.Main" />
+      <option name="VM_PARAMETERS" value="" />
+      <option name="PROGRAM_PARAMETERS" value="" />
+      <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
+      <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
+      <option name="ALTERNATIVE_JRE_PATH" value="" />
+      <option name="ENABLE_SWING_INSPECTOR" value="false" />
+      <option name="ENV_VARIABLES" />
+      <option name="PASS_PARENT_ENVS" value="true" />
+      <module name="launcher" />
+      <envs />
+      <RunnerSettings RunnerId="Debug">
+        <option name="DEBUG_PORT" value="63810" />
+        <option name="TRANSPORT" value="0" />
+        <option name="LOCAL" value="true" />
+      </RunnerSettings>
+      <RunnerSettings RunnerId="Run" />
+      <ConfigurationWrapper RunnerId="Debug" />
+      <ConfigurationWrapper RunnerId="Run" />
+      <method />
+    </configuration>
+''')
+        runManagerConfig.append(appConfig)
     }
 }
 
@@ -321,7 +338,12 @@ 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 })))
-        beforeConfigured { module -> module.dependencies = [] }
+    }
+    eclipseClasspath {
+        plusConfigurations.add(configurations.detachedConfiguration(new DefaultSelfResolvingDependency(files { sourceSets*.resources*.srcDirs*.findAll { it.isDirectory() }} )))
+        whenConfigured { classpath ->
+            classpath.entries.removeAll { it instanceof org.gradle.plugins.ide.eclipse.model.SourceFolder && it.path.endsWith('/resources') }
+        }
     }
 }
 
@@ -333,6 +355,9 @@ configurations {
     plugins {
         visible = false
     }
+    coreImpl {
+        visible = false
+    }
     testRuntime {
         extendsFrom runtime
         extendsFrom plugins
@@ -342,10 +367,11 @@ configurations {
 dependencies {
     runtime runtimeProjects()
     plugins pluginProjects()
+    coreImpl project(':coreImpl')
 }
 
 evaluationDependsOn(':docs')
-evaluationDependsOn(':core')
+evaluationDependsOn(':integTest')
 
 clean.dependsOn subprojects.collect { "$it.path:clean" }
 
@@ -358,7 +384,9 @@ task publishLocalArchives(dependsOn: publishedProjects()*.publishLocalArchives)
 zipRootFolder = "$archivesBaseName-${-> version}"
 
 binDistImage = copySpec {
-    from 'src/toplevel'
+    from('src/toplevel') {
+        expand(version: version)
+    }
     from project(':docs').distDocs.destFile
     into('bin') {
         from startScriptsDir
@@ -369,6 +397,9 @@ binDistImage = copySpec {
         into('plugins') {
             from configurations.plugins - configurations.runtime
         }
+        into('core-impl') {
+            from configurations.coreImpl - configurations.runtime
+        }
     }
 }
 
@@ -439,60 +470,16 @@ task intTestImage(type: Sync) {
     }
 }
 
-tasks.withType(Test).allTasks { task ->
-    task.configure {
-        dependsOn intTestImage, publishLocalArchives, binZip, allZip, srcZip, project(':docs').userguideDocbook
-        integTestUserDir = file('intTestHomeDir')
-        systemProperties['integTest.userGuideInfoDir'] = project(':docs').docbookSrc
-        systemProperties['integTest.userGuideOutputDir'] = new File(project(':docs').samplesSrcDir, "userguideOutput").absolutePath
-        systemProperties['integTest.gradleUserHomeDir'] = integTestUserDir.absolutePath
-        forkEvery = 15
-        maxParallelForks = guessMaxForks()
-
-        testClassesDir = project(':core').sourceSets.integTest.classesDir
-        classpath = project(':core').sourceSets.integTest.runtimeClasspath + configurations.testRuntime
-        testResultsDir = file("build/test-results/$name")
-        testReportDir = file("build/reports/tests/$name")
-        testSrcDirs = []
-
-        systemProperties['integTest.gradleHomeDir'] = intTestImage.integTestGradleHome.absolutePath
-        jvmArgs '-Xmx512m', '-XX:+HeapDumpOnOutOfMemoryError'
-
-        doFirst {
-            if (isDevBuild()) {
-                exclude 'org/gradle/integtests/DistributionIntegrationTest.*'
-            }
-        }
-    }
-}
-
-task integTest(type: Test) {
-}
-
-task embeddedIntegTest(type: Test) {
-    systemProperties['org.gradle.integtest.executer'] = 'embedded'
-    jvmArgs '-Xmx512m', '-XX:MaxPermSize=256m', '-XX:+HeapDumpOnOutOfMemoryError'
+def isDevBuild() {
+    gradle.taskGraph.hasTask(developerBuild)
 }
 
-task daemonIntegTest(type: Test) {
-    systemProperties['org.gradle.integtest.executer'] = 'daemon'
+def isCIBuild() {
+    gradle.taskGraph.hasTask(ciBuild)
 }
 
-private def isDevBuild() {
-    gradle.taskGraph.hasTask(':developerBuild')
-}
-
-gradle.taskGraph.whenReady { graph ->
-    if (isDevBuild()) {
-        if (OperatingSystem.current().isWindows()) {
-            embeddedIntegTest.enabled = false
-        } else {
-            integTest.enabled = false
-        }
-    }
-    if (graph.hasTask(':ciBuild')) {
-        embeddedIntegTest.enabled = false
-    }
+def isCommitBuild() {
+    gradle.taskGraph.hasTask(commitBuild)
 }
 
 def guessMaxForks() {
@@ -500,7 +487,7 @@ def guessMaxForks() {
     return Math.max(2, (int) (processors / 2))
 }
 
-task testedDists(dependsOn: [assemble, check, integTest, embeddedIntegTest, 'openApi:integTest', ':ui:integTest'])
+task testedDists(dependsOn: [assemble, check, integTests])
 
 task nightlyBuild(dependsOn: [clean, testedDists, ':docs:uploadDocs'])
 
@@ -524,10 +511,13 @@ uploadDists {
     dependsOn testedDists
     uploadDescriptor = false
     doFirst {
-        org.apache.ivy.util.url.CredentialsStore.INSTANCE.addCredentials('Artifactory Realm', 'gradle.artifactoryonline.com', artifactoryUserName, artifactoryUserPassword)
-        repositories.add(new org.apache.ivy.plugins.resolver.URLResolver()) {
-            name = 'gradleReleases'
-            addArtifactPattern("${version.distributionUrl}/[artifact]-[revision](-[classifier]).[ext]" as String)
+        repositories {
+            ivy {
+                name = 'gradleReleases'
+                artifactPattern "${version.distributionUrl}/[artifact]-[revision](-[classifier]).[ext]"
+                userName = artifactoryUserName
+                password = artifactoryUserPassword
+            }
         }
     }
 }
@@ -543,13 +533,19 @@ gradle.taskGraph.whenReady {graph ->
 task developerBuild {
     description = 'Builds distributions and runs pre-checkin checks'
     group = 'build'
-    dependsOn clean, testedDists
+    dependsOn testedDists
 }
 
 task ciBuild {
-    description = 'Build performed by the CI server'
+    description = 'Full build performed by the CI server'
     dependsOn clean, testedDists
 }
+
+task commitBuild {
+    description = 'Commit build performed by the CI server'
+    dependsOn testedDists
+}
+
 gradle.taskGraph.whenReady {graph ->
     if (graph.hasTask(ciBuild)) {
         subprojects { reportsDirName = "$rootProject.reportsDir/${path.replaceFirst(':', '').replaceAll(':', '.')}" }
@@ -581,7 +577,7 @@ task release {
 }
 
 task wrapper(type: Wrapper) {
-    gradleVersion = '0.9.1'
+    gradleVersion = '1.0-milestone-2'
 }
 
 def groovyProjects() {
@@ -593,11 +589,25 @@ def publishedProjects() {
 }
 
 def runtimeProjects() {
-    groovyProjects() - pluginProjects()
+    groovyProjects() - pluginProjects() - [project(':integTest'), project(':coreImpl')]
 }
 
 def pluginProjects() {
-    ['plugins', 'codeQuality', 'jetty', 'antlr', 'wrapper', 'osgi', 'maven', 'eclipse', 'idea', 'announce', 'scala'].collect {
+    ['plugins', 'codeQuality', 'jetty', 'antlr', 'wrapper', 'osgi', 'maven', 'ide', 'announce', 'scala', 'sonar'].collect {
         project(it)
     }
 }
+
+switch (System.getProperty("user.name")) {
+    case "pniederw":
+        allprojects {
+            tasks.withType(Test) {
+                maxParallelForks = 1
+                forkEvery = 0
+            }
+        }
+        break
+    default:
+        break
+}
+
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index f7e8b24..1fff211 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -20,14 +20,14 @@ apply plugin: 'idea'
 apply plugin: 'eclipse'
 
 repositories {
-    mavenRepo(urls: 'http://gradle.artifactoryonline.com/gradle/libs')
+    mavenRepo(urls: 'http://repo.gradle.org/gradle/libs')
 }
 
 dependencies {
     compile gradleApi()
     compile 'com.google.collections:google-collections:1.0 at jar'
     groovy localGroovy()
-    testCompile 'junit:junit:4.8.1 at jar'
+    testCompile 'junit:junit:4.8.2 at jar'
     testCompile 'org.spockframework:spock-core:0.5-groovy-1.7 at jar', 'cglib:cglib-nodep:2.2', 'org.objenesis:objenesis:1.2'
 }
 
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/docbook/JavadocScanner.java b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/docbook/JavadocScanner.java
index cf0b342..56e7c2f 100644
--- a/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/docbook/JavadocScanner.java
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/docbook/JavadocScanner.java
@@ -139,7 +139,7 @@ class JavadocScanner {
             BufferedReader reader = new BufferedReader(new StringReader(rawCommentText));
             String line;
             while ((line = reader.readLine()) != null) {
-                line = line.replaceFirst("\\s*\\*\\s*", "");
+                line = line.replaceFirst("\\s*\\* ?", "");
                 if (line.startsWith("@")) {
                     // Ignore the tag section of the comment
                     break;
diff --git a/buildSrc/src/test/groovy/org/gradle/build/docs/XmlSpecification.groovy b/buildSrc/src/test/groovy/org/gradle/build/docs/XmlSpecification.groovy
index 4279cf5..a0ec953 100644
--- a/buildSrc/src/test/groovy/org/gradle/build/docs/XmlSpecification.groovy
+++ b/buildSrc/src/test/groovy/org/gradle/build/docs/XmlSpecification.groovy
@@ -22,7 +22,7 @@ import org.xml.sax.InputSource
 import spock.lang.Specification
 import org.w3c.dom.*
 
-class XmlSpecification extends Specification {
+abstract class XmlSpecification extends Specification {
     final Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument()
 
     def parse(String str) {
diff --git a/buildSrc/src/test/groovy/org/gradle/build/docs/dsl/docbook/JavadocConverterTest.groovy b/buildSrc/src/test/groovy/org/gradle/build/docs/dsl/docbook/JavadocConverterTest.groovy
index 74a359b..54bf335 100644
--- a/buildSrc/src/test/groovy/org/gradle/build/docs/dsl/docbook/JavadocConverterTest.groovy
+++ b/buildSrc/src/test/groovy/org/gradle/build/docs/dsl/docbook/JavadocConverterTest.groovy
@@ -26,6 +26,18 @@ class JavadocConverterTest extends XmlSpecification {
     final GenerationListener listener = Mock()
     final JavadocConverter parser = new JavadocConverter(document, linkConverter)
 
+    def respectsLineIndentation() {
+        _ * classMetaData.rawCommentText >> '''
+ * x
+ *   indented
+'''
+        when:
+        def result = parser.parse(classMetaData, listener)
+
+        then:
+        format(result.docbook).contains('x\n  indented')
+    }
+
     def removesLeadingAsterixFromEachLine() {
         _ * classMetaData.rawCommentText >> ''' * line 1
  * line 2
diff --git a/config/checkstyle/checkstyle-api.xml b/config/checkstyle/checkstyle-api.xml
index e6d6ff6..c646fdd 100644
--- a/config/checkstyle/checkstyle-api.xml
+++ b/config/checkstyle/checkstyle-api.xml
@@ -23,7 +23,10 @@
     <module name="JavadocPackage"/>
 
     <module name="TreeWalker">
-        <module name="JavadocStyle"/>
+        <module name="JavadocStyle">
+            <property name="checkFirstSentence" value="false"/>
+            <property name="checkEmptyJavadoc" value="true"/>
+        </module>
 
         <module name="JavadocType">
             <property name="scope" value="package"/>
diff --git a/config/codenarc.xml b/config/codenarc.xml
index 63ba08d..e130e11 100644
--- a/config/codenarc.xml
+++ b/config/codenarc.xml
@@ -25,14 +25,14 @@
     <ruleset-ref path='rulesets/imports.xml'/>
     <ruleset-ref path='rulesets/naming.xml'>
         <rule-config name='ClassName'>
-            <property name='regex' value='^[A-Z][a-zA-Z0-9]*$'/>
+            <property name='regex' value='^[A-Z][\$a-zA-Z0-9]*$'/>
         </rule-config>
         <rule-config name='FieldName'>
             <property name='finalRegex' value='^[a-z][a-zA-Z0-9]*$'/>
-            <property name='staticFinalRegex' value='^[A-Z][A-Z_0-9]*$'/>
+            <property name='staticFinalRegex' value='^logger$|^[A-Z][A-Z_0-9]*$'/>
         </rule-config>
         <rule-config name='MethodName'>
-            <property name='regex' value='^[a-z].*$'/>
+            <property name='regex' value='^[a-z][\$_a-zA-Z0-9]*$|^.*\s.*$'/>
         </rule-config>
         <rule-config name='VariableName'>
             <property name='finalRegex' value='^[a-z][a-zA-Z0-9]*$'/>
diff --git a/gradle.properties b/gradle.properties
index b2fc51f..022a988 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,2 +1,2 @@
-previousVersion=0.9.1
-nextVersion=0.9.2
+previousVersion=1.0-milestone-2
+nextVersion=1.0-milestone-3
\ No newline at end of file
diff --git a/gradle/codeQuality.gradle b/gradle/codeQuality.gradle
index 093ea87..4e4608e 100644
--- a/gradle/codeQuality.gradle
+++ b/gradle/codeQuality.gradle
@@ -7,8 +7,8 @@ checkstyleConfigFileName = new File(checkstyleConfigDir, "checkstyle.xml")
 codeNarcConfigFileName = "$configDir/codenarc.xml"
 checkstyleProperties.checkstyleConfigDir = checkstyleConfigDir
 
-plugins.withType(GroovyBasePlugin).allObjects {
-    sourceSets.allObjects { sourceSet ->
+plugins.withType(GroovyBasePlugin) {
+    sourceSets.all { sourceSet ->
         task "${sourceSet.getTaskName('checkstyle', 'groovy')}"(type: Checkstyle) {
             configFile = new File(checkstyleConfigDir, "checkstyle-groovy.xml")
             source sourceSet.allGroovy
diff --git a/gradle/compile.gradle b/gradle/compile.gradle
index ce4e34c..ce2a6ed 100644
--- a/gradle/compile.gradle
+++ b/gradle/compile.gradle
@@ -1,8 +1,7 @@
-
-tasks.withType(Compile).allObjects { task ->
-    task.options.encoding = 'utf-8'
+tasks.withType(Compile) {
+    options.encoding = 'utf-8'
 }
-tasks.withType(GroovyCompile).allObjects { task ->
-    task.options.encoding = 'utf-8'
-    task.groovyOptions.encoding = 'utf-8'
+tasks.withType(GroovyCompile) {
+    options.encoding = 'utf-8'
+    groovyOptions.encoding = 'utf-8'
 }
diff --git a/gradle/integTest.gradle b/gradle/integTest.gradle
index c49bc1f..d8b34f1 100644
--- a/gradle/integTest.gradle
+++ b/gradle/integTest.gradle
@@ -1,4 +1,5 @@
 apply plugin: 'java'
+rootProject.apply plugin: IntegTestPlugin
 
 configurations {
     integTestCompile {
@@ -27,3 +28,74 @@ eclipseClasspath {
     plusConfigurations.add(configurations.integTestCompile)
     plusConfigurations.add(configurations.integTestRuntime)
 }
+
+integTestTasks = tasks.withType(Test).matching { it.name.toLowerCase().endsWith('integtest') }
+rootProject.integTests << integTestTasks
+
+integTestTasks.all {
+    dependsOn ':intTestImage'
+    testClassesDir = sourceSets.integTest.classesDir
+    classpath = sourceSets.integTest.runtimeClasspath
+    testSrcDirs = []
+    jvmArgs '-XX:+HeapDumpOnOutOfMemoryError'
+
+    doFirst {
+        testResultsDir = file("${project.testResultsDir}/$name")
+        testReportDir = file("${project.testReportDir}/$name")
+        systemProperties['integTest.gradleHomeDir'] = integTestImageDir.absolutePath
+        systemProperties['integTest.gradleUserHomeDir'] = integTestUserDir.absolutePath
+    }
+}
+
+['embedded', 'forking', 'daemon'].each {
+    def mode = it
+    def taskName = "${it}IntegTest"
+    tasks.addRule(taskName) { name ->
+        if (name != taskName) { return }
+        tasks.add(taskName, Test).configure {
+            systemProperties['org.gradle.integtest.executer'] = mode
+        }
+    }
+}
+
+task integTest(type: Test) {
+    doFirst {
+        systemProperties['org.gradle.integtest.executer'] = integTestMode
+    }
+}
+
+class IntegTestPlugin implements Plugin<Project> {
+    public void apply(Project project) {
+        project.convention.plugins.integTest = new IntegTestConvention(project)
+    }
+}
+
+class IntegTestConvention {
+    private final Project project
+    final List integTests = []
+
+    IntegTestConvention(Project project) {
+        this.project = project
+    }
+
+    String getIntegTestMode() {
+        if (!project.tasks.findByName('ciBuild') || !project.gradle.taskGraph.populated) {
+            return null
+        }
+        if (project.isCIBuild() || OperatingSystem.current().isWindows()) {
+            return 'forking'
+        }
+        return 'embedded'
+    }
+
+    File getIntegTestUserDir() {
+        return project.file('intTestHomeDir')
+    }
+
+    File getIntegTestImageDir() {
+        if (!project.tasks.findByName('intTestImage')) {
+            return null
+        }
+        return project.intTestImage.destinationDir
+    }
+}
diff --git a/gradle/publish.gradle b/gradle/publish.gradle
index 91b7bfb..7c72ead 100644
--- a/gradle/publish.gradle
+++ b/gradle/publish.gradle
@@ -1,7 +1,11 @@
 apply plugin: 'maven'
 
 configurations {
+    publishCompile
     publishRuntime
+    compile {
+        extendsFrom publishCompile
+    }
 }
 
 task sourceJar(type: Jar) {
@@ -13,9 +17,12 @@ task generatePom {
     pomFile = new File(temporaryDir, 'pom.xml')
     doLast {
         dependencies {
-            configurations.runtime.getAllDependencies(ProjectDependency).each {
+            configurations.publishCompile.getAllDependencies(ProjectDependency).each {
                 publishRuntime "org.gradle:gradle-${it.dependencyProject.name}:${version}"
             }
+            configurations.publishCompile.getAllDependencies(ExternalModuleDependency).each {
+                publishRuntime it
+            }
         }
 
         def localDeployer = install.repositories.mavenInstaller
@@ -43,10 +50,13 @@ uploadArchives {
     dependsOn generatePom
     uploadDescriptor = false
     doFirst {
-        org.apache.ivy.util.url.CredentialsStore.INSTANCE.addCredentials('Artifactory Realm', 'gradle.artifactoryonline.com', artifactoryUserName, artifactoryUserPassword)
-        repositories.add(new org.apache.ivy.plugins.resolver.URLResolver()) {
-            name = 'gradleReleases'
-            addArtifactPattern("${version.libsUrl}/${project.group.replaceAll('\\.', '/')}/${archivesBaseName}/[revision]/[artifact]-[revision](-[classifier]).[ext]" as String)
+        repositories {
+            ivy {
+                name = 'gradleLibs'
+                artifactPattern "${version.libsUrl}/${project.group.replaceAll('\\.', '/')}/${archivesBaseName}/[revision]/[artifact]-[revision](-[classifier]).[ext]"
+                userName = artifactoryUserName
+                password = artifactoryUserPassword
+            }
         }
     }
 }
diff --git a/gradle/ssh.gradle b/gradle/ssh.gradle
new file mode 100644
index 0000000..6ca174c
--- /dev/null
+++ b/gradle/ssh.gradle
@@ -0,0 +1,54 @@
+
+project.Scp = Scp.class
+
+configurations {
+    sshAntTask
+}
+
+dependencies {
+    sshAntTask "org.apache.ant:ant-jsch:1.8.2"
+}
+
+tasks.withType(Scp) {
+    sshAntClasspath = configurations.sshAntTask
+}
+
+class Scp extends DefaultTask {
+    @InputFiles
+    FileCollection sshAntClasspath
+
+    def from(Object path) {
+        source << path
+    }
+
+    @InputDirectory
+    File sourceDir
+
+    @Input
+    String host
+
+    @Input
+    String userName
+
+    @Input
+    String password
+
+    @Input
+    String destinationDir
+
+    @TaskAction
+    def executeActions() {
+        ant.taskdef(name: 'scp',
+                classname: 'org.apache.tools.ant.taskdefs.optional.ssh.Scp',
+                classpath: sshAntClasspath.asPath,
+                loaderref: 'ssh')
+        ant.taskdef(name: 'sshexec',
+                classname: 'org.apache.tools.ant.taskdefs.optional.ssh.SSHExec',
+                classpath: sshAntClasspath.asPath,
+                loaderref: 'ssh')
+        ant.sshexec(host: host, username: userName, password: password, command: "mkdir -p ${destinationDir}")
+        ant.scp(remotetodir: "${userName}@${host}:${destinationDir}", password: password) {
+            fileset(dir: sourceDir)
+        }
+    }
+}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index b8f4a02..d17b943 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Mon Jan 03 11:17:24 EST 2011
+#Mon Apr 11 08:31:29 EST 2011
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://gradle.artifactoryonline.com/gradle/distributions/gradle-0.9.1-bin.zip
+distributionUrl=http\://repo.gradle.org/gradle/distributions/gradle-snapshots/gradle-1.0-milestone-3-20110424172210+1000-bin.zip
\ No newline at end of file
diff --git a/gradlew b/gradlew
index 6e0b988..d8809f1 100755
--- a/gradlew
+++ b/gradlew
@@ -12,16 +12,20 @@
 
 GRADLE_APP_NAME=Gradle
 
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
 warn ( ) {
-    echo "${PROGNAME}: $*"
+    echo "$*"
 }
 
 die ( ) {
-    warn "$*"
+    echo
+    echo "$*"
+    echo
     exit 1
 }
 
-
 # OS specific support (must be 'true' or 'false').
 cygwin=false
 msys=false
@@ -79,12 +83,31 @@ if [ -z "$JAVACMD" ] ; then
     fi
 fi
 if [ ! -x "$JAVACMD" ] ; then
-    die "JAVA_HOME is not defined correctly, can not execute: $JAVACMD"
+    die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
 fi
 if [ -z "$JAVA_HOME" ] ; then
     warn "JAVA_HOME environment variable is not set"
 fi
 
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query businessSystem maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+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"
@@ -137,7 +160,7 @@ fi
 
 GRADLE_APP_BASE_NAME=`basename "$0"`
 
-"$JAVACMD" $JAVA_OPTS $GRADLE_OPTS \
+exec "$JAVACMD" $JAVA_OPTS $GRADLE_OPTS \
         -classpath "$CLASSPATH" \
         -Dorg.gradle.appname="$GRADLE_APP_BASE_NAME" \
         -Dorg.gradle.wrapper.properties="$WRAPPER_PROPERTIES" \
diff --git a/gradlew.bat b/gradlew.bat
index 4ceea97..4855abb 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -5,10 +5,6 @@
 @rem                                                                         ##
 @rem ##########################################################################
 
- at rem
- at rem $Revision: 10602 $ $Date: 2008-01-25 02:49:54 +0100 (ven., 25 janv. 2008) $
- at rem
-
 @rem Set local scope for the variables with windows NT shell
 if "%OS%"=="Windows_NT" setlocal
 
@@ -19,69 +15,29 @@ if "%OS%"=="Windows_NT" setlocal
 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 Find java.exe
+set JAVA_EXE=java.exe
+if not defined JAVA_HOME goto init
 
- 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"
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
 
-:check_JAVA_HOME
- at rem Make sure we have a valid JAVA_HOME
-if not "%JAVA_HOME%" == "" goto have_JAVA_HOME
+if exist "%JAVA_EXE%" goto init
 
 echo.
-echo ERROR: Environment variable JAVA_HOME has not been set.
+echo ERROR: JAVA_HOME is 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.
 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
 @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
 @rem Slurp the command line arguments.
 set CMD_LINE_ARGS=
@@ -103,10 +59,10 @@ set CMD_LINE_ARGS=%$
 set STARTER_MAIN_CLASS=org.gradle.wrapper.GradleWrapperMain
 set CLASSPATH=%DIRNAME%\gradle\wrapper\gradle-wrapper.jar
 set WRAPPER_PROPERTIES=%DIRNAME%\gradle\wrapper\gradle-wrapper.properties
-set JAVA_EXE=%JAVA_HOME%\bin\java.exe
 
 set GRADLE_OPTS=%JAVA_OPTS% %GRADLE_OPTS% -Dorg.gradle.wrapper.properties="%WRAPPER_PROPERTIES%"
 
+ at rem Execute Gradle
 "%JAVA_EXE%" %GRADLE_OPTS% -classpath "%CLASSPATH%" %STARTER_MAIN_CLASS% %CMD_LINE_ARGS%
 
 :end
diff --git a/settings.gradle b/settings.gradle
index ffaadfb..e5cc1ba 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 include 'core'
+include 'coreImpl'
 include 'wrapper'
 include 'launcher'
 include 'plugins'
 include 'scala'
-include 'eclipse'
-include 'idea'
+include 'ide'
 include 'osgi'
 include 'maven'
 include 'announce'
@@ -30,6 +30,8 @@ include 'ui'
 include 'openApi'
 include 'toolingApi'
 include 'docs'
+include 'integTest'
+include 'sonar'
 
 rootProject.name = 'gradle'
 rootProject.children.each {project ->
diff --git a/src/toplevel/NOTICE b/src/toplevel/NOTICE
index f049477..7955958 100644
--- a/src/toplevel/NOTICE
+++ b/src/toplevel/NOTICE
@@ -11,8 +11,7 @@ It includes the following other software:
 
 Groovy (http://groovy.codehaus.org)
 Logback (http://logback.qos.ch)
-SL4J (http://www.slf4j.org)
-Svnkit (http://www.svnkit.com)
+SLF4J (http://www.slf4j.org)
 Junit (http://www.junit.org)
 
 For licenses see the LICENSE file.
diff --git a/src/toplevel/changelog.txt b/src/toplevel/changelog.txt
index 3addfe3..171bfcf 100644
--- a/src/toplevel/changelog.txt
+++ b/src/toplevel/changelog.txt
@@ -1,4 +1,4 @@
 
-Release Notes - Gradle - Version 0.9.2
+Release Notes - Gradle - Version ${version}
 
-See http://docs.codehaus.org/display/GRADLE/Gradle+0.9.2+Release+Notes
\ No newline at end of file
+See http://docs.codehaus.org/display/GRADLE/Gradle+${version}+Release+Notes
\ No newline at end of file
diff --git a/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/AnnouncePlugin.groovy b/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/AnnouncePlugin.groovy
index 205488b..3833e20 100644
--- a/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/AnnouncePlugin.groovy
+++ b/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/AnnouncePlugin.groovy
@@ -14,59 +14,67 @@
  * limitations under the License.
  */
 
-package org.gradle.api.plugins.announce;
-
+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
+import org.gradle.api.logging.Logging
+import org.gradle.api.logging.Logger
 
 /**
- * This plugin allows to send announce messages to twitter.
+ * This plugin allows to send announce messages to Twitter.
  *
  * @author hackergarten
  */
 class AnnouncePlugin implements Plugin<Project> {
-    public void apply(final Project project) {
+    void apply(Project project) {
         project.convention.plugins.announce = new AnnouncePluginConvention(project)
     }
 }
 
 class AnnouncePluginConvention {
+    private static final Logger logger = Logging.getLogger(AnnouncePlugin)
+
     /**
-     * The username to use for announcements
+     * The username to use for announcements.
      */
     String username
 
     /**
-     * The password to use for announcements
+     * The password to use for announcements.
      */
     String password
 
     Project project
     AnnouncerFactory announcerFactory
 
-    def AnnouncePluginConvention(project) {
-        this.project = project;
+    AnnouncePluginConvention(project) {
+        this.project = project
         this.announcerFactory = new DefaultAnnouncerFactory(this)
     }
 
     /**
      * Configures the announce plugin convention. The given closure configures the {@link AnnouncePluginConvention}.
      */
-    def announce(Closure closure) {
+    void announce(Closure closure) {
         closure.delegate = this
         closure()
     }
 
     /**
      * Sends an announcement of the given type.
+     *
      * @param msg The content of the announcement
      * @param type The announcement type.
      */
-    def announce(String msg, def type) {
-        announcerFactory.createAnnouncer(type).send(project.name, msg)
+    void announce(String msg, String type) {
+        try {
+            announcerFactory.createAnnouncer(type).send(project.name, msg)
+        } catch (Exception e) {
+            logger.warn("Failed to send message '$msg' to '$type'", e)
+        }
     }
 }
 
diff --git a/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/Announcer.java b/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/Announcer.java
index 24be828..4afd89b 100644
--- a/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/Announcer.java
+++ b/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/Announcer.java
@@ -20,5 +20,5 @@ package org.gradle.api.plugins.announce;
  * An {@code Announcer} allows messages to be sent.
  */
 public interface Announcer {
-  void send(String title, String message); 
+    void send(String title, String message);
 }
diff --git a/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/AnnouncerFactory.groovy b/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/AnnouncerFactory.groovy
new file mode 100644
index 0000000..edd5270
--- /dev/null
+++ b/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/AnnouncerFactory.groovy
@@ -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
+ */
+interface AnnouncerFactory {
+    Announcer createAnnouncer(String type)
+}
diff --git a/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/AnnouncerFactory.java b/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/AnnouncerFactory.java
deleted file mode 100644
index 4c76454..0000000
--- a/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/AnnouncerFactory.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.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/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/DefaultAnnouncerFactory.groovy b/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/DefaultAnnouncerFactory.groovy
index d8e75cc..a4b62ae 100644
--- a/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/DefaultAnnouncerFactory.groovy
+++ b/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/DefaultAnnouncerFactory.groovy
@@ -17,35 +17,38 @@ package org.gradle.api.plugins.announce.internal
 
 import org.gradle.api.plugins.announce.AnnouncePluginConvention
 import org.gradle.api.plugins.announce.Announcer
+import org.gradle.api.InvalidUserDataException
 
 /**
  * @author Hans Dockter
  */
 class DefaultAnnouncerFactory implements AnnouncerFactory {
-    AnnouncePluginConvention announcePluginConvention
+    private final AnnouncePluginConvention announcePluginConvention
 
-    def DefaultAnnouncerFactory(announcePluginConvention) {
-        this.announcePluginConvention = announcePluginConvention;
+    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()
+        switch (type) {
+            case "twitter":
+                String username = announcePluginConvention.username
+                String password = announcePluginConvention.password
+                return new Twitter(username, password)
+            case "notify-send":
+                return new NotifySend(announcePluginConvention.project)
+            case "snarl":
+                return new Snarl()
+            case "growl":
+                return new Growl(announcePluginConvention.project)
+            default:
+                return new UnknownAnnouncer()
         }
-        new DoNothingAnnouncer()
     }
 }
 
-class DoNothingAnnouncer implements Announcer {
+class UnknownAnnouncer implements Announcer {
     void send(String title, String message) {
-        // do nothing
+        throw new InvalidUserDataException("unknown announcer type")
     }
 }
diff --git a/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/Growl.groovy b/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/Growl.groovy
index 9021d66..29be1cc 100644
--- a/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/Growl.groovy
+++ b/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/Growl.groovy
@@ -15,15 +15,20 @@
  */
 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
+import org.gradle.api.Project
 
 class Growl implements Announcer {
+    private final Project project
+
+    Growl(Project project) {
+        this.project = project
+    }
+
     void send(String title, String message) {
-        ExecAction execAction = new DefaultExecAction()
-        execAction.executable('growlnotify')
-        execAction.args('-m', message, title)
-        execAction.execute()
+        project.exec {
+            executable 'growlnotify'
+            args '-m', message, title
+        }
     }
 }
diff --git a/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/NotifySend.groovy b/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/NotifySend.groovy
index f683a27..1e5a89a 100644
--- a/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/NotifySend.groovy
+++ b/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/NotifySend.groovy
@@ -19,6 +19,7 @@ package org.gradle.api.plugins.announce.internal;
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 import org.gradle.api.plugins.announce.Announcer
+import org.gradle.api.Project
 
 /**
  * This class wraps the Ubuntu Notify Send functionality.
@@ -27,23 +28,18 @@ import org.gradle.api.plugins.announce.Announcer
  */
 
 class NotifySend implements Announcer {
+    private static final Logger logger = LoggerFactory.getLogger(NotifySend)
 
-    private static final Logger LOGGER = LoggerFactory.getLogger(NotifySend)
+    private final Project project
 
-    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''')
+    NotifySend(Project project) {
+        this.project = project
+    }
 
+    void send(String title, String message) {
+        project.exec {
+            executable 'notify-send'
+            args title, message
         }
     }
 }
diff --git a/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/Snarl.groovy b/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/Snarl.groovy
index 37d24c9..85f46a1 100644
--- a/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/Snarl.groovy
+++ b/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/Snarl.groovy
@@ -19,54 +19,54 @@ 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
+    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 title, String message) {
+        send("localhost", 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))
-      }
+    public void send(Collection hosts, String title, String message) {
+        hosts.each { host ->
+            send(host, 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")]
+    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))
+            }
+        }
+    }
 
-    HEAD + properties.join('') + "\r\n"
-  }
+    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")]
 
-  private String formatProperty(String name, String value) {
-    if (value) {
-      return "#?" + name + "=" + value
+        HEAD + properties.join('') + "\r\n"
     }
-    else {
-      return ""
+
+    private String formatProperty(String name, String value) {
+        if (value) {
+            return "#?" + name + "=" + value
+        }
+        else {
+            return ""
+        }
     }
-  }
 
-  private with(closable, closure) {
-    try {
-      closure(closable)
-    } finally {
-      closable.close()
+    private with(closable, closure) {
+        try {
+            closure(closable)
+        } finally {
+            closable.close()
+        }
     }
-  }
 }
diff --git a/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/Twitter.groovy b/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/Twitter.groovy
index fa2cbbd..a697f15 100644
--- a/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/Twitter.groovy
+++ b/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/Twitter.groovy
@@ -16,55 +16,50 @@
 
 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.
+ * This class allows to send announce messages to Twitter.
  *
- * @author hackergarten
+ * @author Hackergarten
  */
 class Twitter implements Announcer {
+    private static final Logger logger = LoggerFactory.getLogger(Twitter)
 
-  private static final String TWITTER_UPDATE_URL = "https://twitter.com/statuses/update.xml"
+    private final String username
+    private final String password
+
+    Twitter(String username, String password) {
+        this.username = username
+        this.password = password
+    }
 
-  def username
-  def password
+    void send(String title, String message) {
+        HttpURLConnection connection
 
-  private static Logger logger = LoggerFactory.getLogger(Twitter)
+        try {
+            connection = new URL("https://twitter.com/statuses/update.xml").openConnection()
+            connection.requestMethod = "POST"
+            connection.doInput = true
+            connection.doOutput = true
+            connection.useCaches = false
 
-  Twitter(String username, String password) {
-    this.username = username
-    this.password = password
-  }
+            String credentials = new BASE64Encoder().encodeBuffer("$username:$password".toString().getBytes("UTF-8")).trim()
+            connection.setRequestProperty "Authorization", "Basic " + credentials
 
-  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()
+            connection.outputStream.withWriter { out ->
+                out.write "status=" + URLEncoder.encode(message, "UTF-8")
+            }
 
+            logger.info("Successfully tweeted '$message' using account '$username'")
+            if (logger.debugEnabled) {
+                logger.debug connection.inputStream.getText("UTF-8")
+            }
+        } finally {
+            connection?.disconnect()
+        }
     }
-  
-  }
 }
diff --git a/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/package-info.java b/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/package-info.java
index 6ac0034..e45e10e 100644
--- a/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/package-info.java
+++ b/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/package-info.java
@@ -15,6 +15,6 @@
  */
 
 /**
- * A {link org.gradle.api.Plugin} for generating announcements from your build. 
+ * A {@link org.gradle.api.Plugin} for generating announcements from your build.
  */
 package org.gradle.api.plugins.announce;
diff --git a/subprojects/announce/src/main/resources/META-INF/gradle-plugins/announce.properties b/subprojects/announce/src/main/resources/META-INF/gradle-plugins/announce.properties
index f236da0..b0b0e75 100644
--- a/subprojects/announce/src/main/resources/META-INF/gradle-plugins/announce.properties
+++ b/subprojects/announce/src/main/resources/META-INF/gradle-plugins/announce.properties
@@ -1,16 +1 @@
-#
-# Copyright 2010 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
 implementation-class=org.gradle.api.plugins.announce.AnnouncePlugin
diff --git a/subprojects/announce/src/test/groovy/org/gradle/api/plugins/announce/internal/DefaultAnnouncerFactoryTest.groovy b/subprojects/announce/src/test/groovy/org/gradle/api/plugins/announce/internal/DefaultAnnouncerFactoryTest.groovy
index 9c8898f..e44e45e 100644
--- a/subprojects/announce/src/test/groovy/org/gradle/api/plugins/announce/internal/DefaultAnnouncerFactoryTest.groovy
+++ b/subprojects/announce/src/test/groovy/org/gradle/api/plugins/announce/internal/DefaultAnnouncerFactoryTest.groovy
@@ -23,7 +23,6 @@ import org.gradle.api.plugins.announce.AnnouncePluginConvention
 /**
  * @author Hans Dockter
  */
-
 class DefaultAnnouncerFactoryTest extends Specification {
     AnnouncePluginConvention announcePluginConvention = new AnnouncePluginConvention(project)
     DefaultAnnouncerFactory announcerFactory = new DefaultAnnouncerFactory(announcePluginConvention)
@@ -32,7 +31,7 @@ class DefaultAnnouncerFactoryTest extends Specification {
     def createForTwitter() {
         announcePluginConvention.username = 'username'
         announcePluginConvention.password = 'password'
-        
+
         when:
         Twitter twitter = announcerFactory.createAnnouncer('twitter')
 
@@ -58,6 +57,6 @@ class DefaultAnnouncerFactoryTest extends Specification {
 
     def createWithUnknownType() {
         expect:
-        announcerFactory.createAnnouncer('unknown') instanceof DoNothingAnnouncer
+        announcerFactory.createAnnouncer('inter-galaxy-announcer') instanceof UnknownAnnouncer
     }
 }
\ No newline at end of file
diff --git a/subprojects/announce/src/test/groovy/org/gradle/api/plugins/announce/internal/NotifySendTest.groovy b/subprojects/announce/src/test/groovy/org/gradle/api/plugins/announce/internal/NotifySendTest.groovy
index 51563f6..5cd45a6 100644
--- a/subprojects/announce/src/test/groovy/org/gradle/api/plugins/announce/internal/NotifySendTest.groovy
+++ b/subprojects/announce/src/test/groovy/org/gradle/api/plugins/announce/internal/NotifySendTest.groovy
@@ -15,44 +15,26 @@
  */
 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"
+import org.gradle.api.Project
+import spock.lang.Specification
+import org.gradle.util.ConfigureUtil
+import org.gradle.process.internal.ExecHandleBuilder
+import org.gradle.process.ExecResult
+
+class NotifySendTest extends Specification {
+    def "sending of an announcement invokes notify-send command"() {
+        def execClosure
+        def project = Mock(Project)
+        def notifier = new NotifySend(project)
+
+        when:
+        notifier.send("title", "body")
+
+        then:
+        1 * project.exec(!null) >> { execClosure = it[0]; Mock(ExecResult) }
+        def execSpec = ConfigureUtil.configure(execClosure, new ExecHandleBuilder())
+        execSpec.executable == 'notify-send'
+        execSpec.args.contains 'title'
+        execSpec.args.contains 'body'
     }
-  }*/
-
-  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/announce/src/test/groovy/org/gradle/api/plugins/announce/internal/SnarlTest.groovy b/subprojects/announce/src/test/groovy/org/gradle/api/plugins/announce/internal/SnarlTest.groovy
index addd85e..63d5cbc 100644
--- a/subprojects/announce/src/test/groovy/org/gradle/api/plugins/announce/internal/SnarlTest.groovy
+++ b/subprojects/announce/src/test/groovy/org/gradle/api/plugins/announce/internal/SnarlTest.groovy
@@ -24,18 +24,18 @@ class SnarlTest extends GroovyTestCase {
 //		new Snarl().send(["localhost", "localhost", "localhost"], "JUnit Test", "Hello from Groovy!");
 //	}
 
-	void testMockSend() {
+    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 capture
 
-  static void println(PrintWriter p, String input) {
-    capture = input
-  }
+    static void println(PrintWriter p, String input) {
+        capture = input
+    }
 }
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrPlugin.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrPlugin.java
index 29ffdf3..da6089b 100644
--- a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrPlugin.java
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrPlugin.java
@@ -66,7 +66,7 @@ public class AntlrPlugin implements Plugin<Project> {
                                 AntlrSourceVirtualDirectory.NAME, antlrDirectoryDelegate);
                         final String srcDir = String.format("src/%s/antlr", sourceSet.getName());
                         antlrDirectoryDelegate.getAntlr().srcDir(srcDir);
-                        sourceSet.getAllSource().add(antlrDirectoryDelegate.getAntlr());
+                        sourceSet.getAllSource().source(antlrDirectoryDelegate.getAntlr());
 
                         // 2) create an AntlrTask for this sourceSet following the gradle
                         //    naming conventions via call to sourceSet.getTaskName()
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrSourceVirtualDirectory.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrSourceVirtualDirectory.java
index 80a0b1e..2907b23 100644
--- a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrSourceVirtualDirectory.java
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrSourceVirtualDirectory.java
@@ -17,7 +17,6 @@
 package org.gradle.api.plugins.antlr;
 
 import groovy.lang.Closure;
-import org.gradle.api.file.FileTree;
 import org.gradle.api.file.SourceDirectorySet;
 
 /**
@@ -47,10 +46,4 @@ public interface AntlrSourceVirtualDirectory {
      */
     public AntlrSourceVirtualDirectory antlr(Closure configureClosure);
 
-    /**
-     * All Antlr source for this source set.
-     *
-     * @return The Antlr source. Never returns null.
-     */
-    public FileTree getAllAntlr();
 }
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrSourceVirtualDirectoryImpl.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrSourceVirtualDirectoryImpl.java
index b891c9a..27dced5 100644
--- a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrSourceVirtualDirectoryImpl.java
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrSourceVirtualDirectoryImpl.java
@@ -15,15 +15,12 @@
  */
 package org.gradle.api.plugins.antlr.internal;
 
+import groovy.lang.Closure;
 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.
@@ -32,15 +29,11 @@ import groovy.lang.Closure;
  */
 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() {
@@ -51,12 +44,4 @@ public class AntlrSourceVirtualDirectoryImpl implements AntlrSourceVirtualDirect
         ConfigureUtil.configure(configureClosure, getAntlr());
         return this;
     }
-
-    public UnionFileTree getAllAntlr() {
-        return allAntlr;
-    }
-
-    public PatternFilterable getAntlrSourcePatterns() {
-        return antlrPatterns;
-    }
 }
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/package-info.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/package-info.java
index 8378926..178bdb7 100644
--- a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/package-info.java
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/package-info.java
@@ -15,6 +15,6 @@
  */
 
 /**
- * A {link org.gradle.api.Plugin} for generating parsers from Antlr grammars.
+ * A {@link org.gradle.api.Plugin} for generating parsers from Antlr grammars.
  */
 package org.gradle.api.plugins.antlr;
diff --git a/subprojects/antlr/src/main/resources/META-INF/gradle-plugins/antlr.properties b/subprojects/antlr/src/main/resources/META-INF/gradle-plugins/antlr.properties
index f31f7fa..4ea794c 100644
--- a/subprojects/antlr/src/main/resources/META-INF/gradle-plugins/antlr.properties
+++ b/subprojects/antlr/src/main/resources/META-INF/gradle-plugins/antlr.properties
@@ -1,16 +1 @@
-#
-# Copyright 2010 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
 implementation-class=org.gradle.api.plugins.antlr.AntlrPlugin
diff --git a/subprojects/code-quality/code-quality.gradle b/subprojects/code-quality/code-quality.gradle
index b433c56..833e67b 100644
--- a/subprojects/code-quality/code-quality.gradle
+++ b/subprojects/code-quality/code-quality.gradle
@@ -19,7 +19,7 @@ dependencies {
     compile project(':core')
     compile project(':plugins')
 
-    compile "org.codenarc:CodeNarc:0.12 at jar"
+    compile "org.codenarc:CodeNarc:0.13 at jar"
     compile libraries.slf4j_api
 
     // CodeNarc dependencies
@@ -28,7 +28,7 @@ dependencies {
 
     // Checkstyle dependencies
     runtime "com.puppycrawl.tools:checkstyle:5.3 at jar",
-            libraries.google_collections,
+            libraries.guava,
             libraries.antlr,
             "commons-beanutils:commons-beanutils-core:1.8.3 at jar"
 
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/AntCheckstyle.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/AntCheckstyle.groovy
deleted file mode 100644
index b91b518..0000000
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/AntCheckstyle.groovy
+++ /dev/null
@@ -1,42 +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.quality
-
-import org.gradle.api.AntBuilder
-import org.gradle.api.GradleException
-import org.gradle.api.file.FileCollection
-import org.gradle.api.tasks.AntBuilderAware
-
-class AntCheckstyle {
-    def checkstyle(AntBuilder ant, FileCollection source, File configFile, File resultFile, AntBuilderAware classpath, Map<String, ?> properties, boolean ignoreFailures) {
-        String propertyName = "org.gradle.checkstyle.violations"
-
-        ant.project.addTaskDefinition('checkstyle', getClass().classLoader.loadClass('com.puppycrawl.tools.checkstyle.CheckStyleTask'))
-        ant.checkstyle(config: configFile, failOnViolation: false, failureProperty: propertyName) {
-            source.addToAntBuilder(ant, 'fileset', FileCollection.AntType.FileSet)
-            classpath.addToAntBuilder(ant, 'classpath')
-            formatter(type: 'plain', useFile: false)
-            formatter(type: 'xml', toFile: resultFile)
-            properties.each {key, value ->
-                property(key: key, value: value.toString())
-            }
-        }
-
-        if (!ignoreFailures && ant.properties[propertyName]) {
-            throw new GradleException("Checkstyle check violations were found in $source. See the report at $resultFile.")
-        }
-    }
-}
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/AntCodeNarc.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/AntCodeNarc.groovy
deleted file mode 100644
index 953e745..0000000
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/AntCodeNarc.groovy
+++ /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.api.plugins.quality
-
-import org.apache.tools.ant.BuildException
-import org.codenarc.ant.CodeNarcTask
-import org.gradle.api.AntBuilder
-import org.gradle.api.GradleException
-import org.gradle.api.file.FileCollection
-
-class AntCodeNarc {
-    def execute(AntBuilder ant, FileCollection source, File configFile, File reportFile, boolean ignoreFailures) {
-        ant.project.addTaskDefinition('codenarc', CodeNarcTask)
-        try {
-            ant.codenarc(ruleSetFiles: "file:$configFile", maxPriority1Violations: 0, maxPriority2Violations: 0, maxPriority3Violations: 0) {
-                report(type: 'html', toFile: reportFile)
-                source.addToAntBuilder(ant, 'fileset', FileCollection.AntType.FileSet)
-            }
-        } catch (BuildException e) {
-            if (e.message.matches('Exceeded maximum number of priority \\d* violations.*')) {
-                if (ignoreFailures) {
-                    return
-                }
-                throw new GradleException("CodeNarc check violations were found in $source. See the report at $reportFile.", e)
-            }
-            throw e
-        }
-    }
-}
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/Checkstyle.java b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/Checkstyle.java
index 1789884..2dce827 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/Checkstyle.java
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/Checkstyle.java
@@ -16,6 +16,7 @@
 package org.gradle.api.plugins.quality;
 
 import org.gradle.api.file.FileCollection;
+import org.gradle.api.plugins.quality.internal.AntCheckstyle;
 import org.gradle.api.tasks.*;
 
 import java.io.File;
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarc.java b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarc.java
index 192b931..c124c85 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarc.java
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarc.java
@@ -16,6 +16,7 @@
 package org.gradle.api.plugins.quality;
 
 import org.gradle.api.logging.LogLevel;
+import org.gradle.api.plugins.quality.internal.AntCodeNarc;
 import org.gradle.api.tasks.*;
 
 import java.io.File;
@@ -26,14 +27,15 @@ import java.io.File;
 public class CodeNarc extends SourceTask implements VerificationTask {
     private AntCodeNarc antCodeNarc = new AntCodeNarc();
 
-    private File reportFile;
     private File configFile;
+    private String reportFormat;
+    private File reportFile;
     private boolean ignoreFailures;
 
     @TaskAction
     public void check() {
         getLogging().captureStandardOutput(LogLevel.INFO);
-        antCodeNarc.execute(getAnt(), getSource(), getConfigFile(), getReportFile(), isIgnoreFailures());
+        antCodeNarc.execute(getAnt(), getSource(), getConfigFile(), getReportFormat(), getReportFile(), isIgnoreFailures());
     }
 
     /**
@@ -56,9 +58,28 @@ public class CodeNarc extends SourceTask implements VerificationTask {
     }
 
     /**
-     * Returns the file to write the HTML report to.
+     * Returns the format type of the CodeNarc report.
+     *
+     * @return The format type of the CodeNarc report.
+     */
+    @Input
+    public String getReportFormat() {
+        return reportFormat;
+    }
+
+    /**
+     * Specifies the format type of the CodeNarc report.
+     *
+     * @param reportFormat The format type of the CodeNarc report.
+     */
+    public void setReportFormat(String reportFormat) {
+        this.reportFormat = reportFormat;
+    }
+
+    /**
+     * Returns the file to write the report to.
      *
-     * @return The HTML report file. Must not be null.
+     * @return The report file. Must not be null.
      */
     @OutputFile
     public File getReportFile() {
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeQualityPlugin.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeQualityPlugin.groovy
index 7e17388..e13775a 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeQualityPlugin.groovy
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeQualityPlugin.groovy
@@ -14,12 +14,10 @@
  * limitations under the License.
  */
 
-
 package org.gradle.api.plugins.quality
 
 import org.gradle.api.Plugin
 import org.gradle.api.Project
-import org.gradle.api.Task
 import org.gradle.api.plugins.GroovyBasePlugin
 import org.gradle.api.plugins.JavaBasePlugin
 import org.gradle.api.plugins.JavaPluginConvention
@@ -31,72 +29,73 @@ import org.gradle.api.tasks.SourceSet
  * A {@link Plugin} which measures and enforces code quality for Java and Groovy projects.
  */
 public class CodeQualityPlugin implements Plugin<Project> {
-    public static final String CHECKSTYLE_MAIN_TASK = "checkstyleMain";
-    public static final String CHECKSTYLE_TEST_TASK = "checkstyleTest";
-    public static final String CODE_NARC_MAIN_TASK = "codenarcMain";
-    public static final String CODE_NARC_TEST_TASK = "codenarcTest";
+    static final String CHECKSTYLE_MAIN_TASK = "checkstyleMain"
+    static final String CHECKSTYLE_TEST_TASK = "checkstyleTest"
+    static final String CODE_NARC_MAIN_TASK = "codenarcMain"
+    static final String CODE_NARC_TEST_TASK = "codenarcTest"
 
-    public void apply(final Project project) {
-        project.plugins.apply(ReportingBasePlugin.class);
+    void apply(Project project) {
+        project.plugins.apply(ReportingBasePlugin)
 
-        JavaCodeQualityPluginConvention javaPluginConvention = new JavaCodeQualityPluginConvention(project)
-        project.convention.plugins.javaCodeQuality = javaPluginConvention;
+        def javaPluginConvention = new JavaCodeQualityPluginConvention(project)
+        project.convention.plugins.javaCodeQuality = javaPluginConvention
 
-        GroovyCodeQualityPluginConvention groovyPluginConvention = new GroovyCodeQualityPluginConvention(project)
-        project.convention.plugins.groovyCodeQuality = groovyPluginConvention;
+        def groovyPluginConvention = new GroovyCodeQualityPluginConvention(project)
+        project.convention.plugins.groovyCodeQuality = groovyPluginConvention
 
         configureCheckstyleDefaults(project, javaPluginConvention)
         configureCodeNarcDefaults(project, groovyPluginConvention)
 
-        project.plugins.withType(JavaBasePlugin.class) {
-            configureForJavaPlugin(project, javaPluginConvention);
+        project.plugins.withType(JavaBasePlugin) {
+            configureForJavaPlugin(project, javaPluginConvention)
         }
-        project.plugins.withType(GroovyBasePlugin.class) {
-            configureForGroovyPlugin(project, groovyPluginConvention);
+        project.plugins.withType(GroovyBasePlugin) {
+            configureForGroovyPlugin(project, groovyPluginConvention)
         }
     }
 
     private void configureCheckstyleDefaults(Project project, JavaCodeQualityPluginConvention pluginConvention) {
-        project.tasks.withType(Checkstyle.class) {Checkstyle checkstyle ->
+        project.tasks.withType(Checkstyle) { Checkstyle checkstyle ->
             checkstyle.conventionMapping.configFile = { pluginConvention.checkstyleConfigFile }
             checkstyle.conventionMapping.map('properties') { pluginConvention.checkstyleProperties }
         }
     }
 
     private void configureCodeNarcDefaults(Project project, GroovyCodeQualityPluginConvention pluginConvention) {
-        project.tasks.withType(CodeNarc.class) {CodeNarc codenarc ->
+        project.tasks.withType(CodeNarc) { CodeNarc codenarc ->
             codenarc.conventionMapping.configFile = { pluginConvention.codeNarcConfigFile }
         }
     }
 
     private void configureCheckTask(Project project) {
-        Task task = project.tasks[JavaBasePlugin.CHECK_TASK_NAME]
-        task.setDescription("Executes all quality checks");
-        task.dependsOn project.tasks.withType(Checkstyle.class)
-        task.dependsOn project.tasks.withType(CodeNarc.class)
+        def task = project.tasks[JavaBasePlugin.CHECK_TASK_NAME]
+        task.description = "Executes all quality checks"
+        task.dependsOn project.tasks.withType(Checkstyle)
+        task.dependsOn project.tasks.withType(CodeNarc)
     }
 
     private void configureForJavaPlugin(Project project, JavaCodeQualityPluginConvention pluginConvention) {
-        configureCheckTask(project);
+        configureCheckTask(project)
 
-        project.convention.getPlugin(JavaPluginConvention.class).sourceSets.all {SourceSet set ->
-            Checkstyle checkstyle = project.tasks.add(set.getTaskName("checkstyle", null), Checkstyle.class);
+        project.convention.getPlugin(JavaPluginConvention).sourceSets.all {SourceSet set ->
+            def checkstyle = project.tasks.add(set.getTaskName("checkstyle", null), Checkstyle)
             checkstyle.description = "Runs Checkstyle against the $set.name Java source code."
-            checkstyle.conventionMapping.defaultSource = { set.allJava; }
+            checkstyle.conventionMapping.defaultSource = { set.allJava }
             checkstyle.conventionMapping.configFile = { pluginConvention.checkstyleConfigFile }
             checkstyle.conventionMapping.resultFile = { new File(pluginConvention.checkstyleResultsDir, "${set.name}.xml") }
-            checkstyle.conventionMapping.classpath = { set.compileClasspath; }
+            checkstyle.conventionMapping.classpath = { set.compileClasspath }
         }
     }
 
     private void configureForGroovyPlugin(Project project, GroovyCodeQualityPluginConvention pluginConvention) {
-        project.convention.getPlugin(JavaPluginConvention.class).sourceSets.all {SourceSet set ->
-            GroovySourceSet groovySourceSet = set.convention.getPlugin(GroovySourceSet.class)
-            CodeNarc codeNarc = project.tasks.add(set.getTaskName("codenarc", null), CodeNarc.class);
-            codeNarc.setDescription("Runs CodeNarc against the $set.name Groovy source code.");
-            codeNarc.conventionMapping.defaultSource = { groovySourceSet.allGroovy; }
-            codeNarc.conventionMapping.configFile = { pluginConvention.codeNarcConfigFile; }
-            codeNarc.conventionMapping.reportFile = { new File(pluginConvention.codeNarcReportsDir, "${set.name}.html"); }
+        project.convention.getPlugin(JavaPluginConvention).sourceSets.all {SourceSet set ->
+            def groovySourceSet = set.convention.getPlugin(GroovySourceSet)
+            def codeNarc = project.tasks.add(set.getTaskName("codenarc", null), CodeNarc)
+            codeNarc.description = "Runs CodeNarc against the $set.name Groovy source code."
+            codeNarc.conventionMapping.defaultSource = { groovySourceSet.allGroovy }
+            codeNarc.conventionMapping.configFile = { pluginConvention.codeNarcConfigFile }
+            codeNarc.conventionMapping.reportFormat = { pluginConvention.codeNarcReportsFormat }
+            codeNarc.conventionMapping.reportFile = { new File(pluginConvention.codeNarcReportsDir, "${set.name}.${pluginConvention.codeNarcReportsFormat}") }
         }
     }
 }
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/GroovyCodeQualityPluginConvention.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/GroovyCodeQualityPluginConvention.groovy
index 96563d8..34beb60 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/GroovyCodeQualityPluginConvention.groovy
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/GroovyCodeQualityPluginConvention.groovy
@@ -25,14 +25,21 @@ class GroovyCodeQualityPluginConvention {
     String codeNarcConfigFileName
 
     /**
+     * The output format of the generated CodeNarc report.
+     */
+    String codeNarcReportsFormat
+
+    /**
      * The name of the directory to write CodeNarc reports into.
      */
     String codeNarcReportsDirName
+
     private final ProjectInternal project
 
     def GroovyCodeQualityPluginConvention(Project project) {
         this.project = project
         codeNarcConfigFileName = 'config/codenarc/codenarc.xml'
+        codeNarcReportsFormat = "html"
         codeNarcReportsDirName = 'codenarc'
     }
 
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/AntCheckstyle.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/AntCheckstyle.groovy
new file mode 100644
index 0000000..3af0d29
--- /dev/null
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/AntCheckstyle.groovy
@@ -0,0 +1,42 @@
+/*
+ * 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.quality.internal
+
+import org.gradle.api.AntBuilder
+import org.gradle.api.GradleException
+import org.gradle.api.file.FileCollection
+import org.gradle.api.tasks.AntBuilderAware
+
+class AntCheckstyle {
+    def checkstyle(AntBuilder ant, FileCollection source, File configFile, File resultFile, AntBuilderAware classpath, Map<String, ?> properties, boolean ignoreFailures) {
+        String propertyName = "org.gradle.checkstyle.violations"
+
+        ant.project.addTaskDefinition('checkstyle', getClass().classLoader.loadClass('com.puppycrawl.tools.checkstyle.CheckStyleTask'))
+        ant.checkstyle(config: configFile, failOnViolation: false, failureProperty: propertyName) {
+            source.addToAntBuilder(ant, 'fileset', FileCollection.AntType.FileSet)
+            classpath.addToAntBuilder(ant, 'classpath')
+            formatter(type: 'plain', useFile: false)
+            formatter(type: 'xml', toFile: resultFile)
+            properties.each {key, value ->
+                property(key: key, value: value.toString())
+            }
+        }
+
+        if (!ignoreFailures && ant.properties[propertyName]) {
+            throw new GradleException("Checkstyle check violations were found in $source. See the report at $resultFile.")
+        }
+    }
+}
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/AntCodeNarc.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/AntCodeNarc.groovy
new file mode 100644
index 0000000..c9e1940
--- /dev/null
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/AntCodeNarc.groovy
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.quality.internal
+
+import org.apache.tools.ant.BuildException
+import org.codenarc.ant.CodeNarcTask
+import org.gradle.api.AntBuilder
+import org.gradle.api.GradleException
+import org.gradle.api.file.FileCollection
+
+class AntCodeNarc {
+    def execute(AntBuilder ant, FileCollection source, File configFile, String reportFormat, File reportFile, boolean ignoreFailures) {
+        ant.project.addTaskDefinition('codenarc', CodeNarcTask)
+        try {
+            ant.codenarc(ruleSetFiles: "file:$configFile", maxPriority1Violations: 0, maxPriority2Violations: 0, maxPriority3Violations: 0) {
+                report(type: ConsoleReportWriter.name)
+                report(type: reportFormat) {
+                    option(name: 'outputFile', value: reportFile)
+                }
+                source.addToAntBuilder(ant, 'fileset', FileCollection.AntType.FileSet)
+            }
+        } catch (BuildException e) {
+            if (e.message.matches('Exceeded maximum number of priority \\d* violations.*')) {
+                if (ignoreFailures) {
+                    return
+                }
+                throw new GradleException("CodeNarc check violations were found in $source. See the report at $reportFile.", e)
+            }
+            throw e
+        }
+    }
+}
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/ConsoleReportWriter.java b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/ConsoleReportWriter.java
new file mode 100644
index 0000000..bdec2dc
--- /dev/null
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/ConsoleReportWriter.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.quality.internal;
+
+import org.codenarc.AnalysisContext;
+import org.codenarc.report.ReportWriter;
+import org.codenarc.results.Results;
+import org.codenarc.rule.Violation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Formatter;
+
+public class ConsoleReportWriter implements ReportWriter {
+    private static final Logger LOGGER = LoggerFactory.getLogger(ConsoleReportWriter.class);
+
+    public void writeReport(AnalysisContext analysisContext, Results results) {
+        report(results);
+    }
+
+    private void report(Results results) {
+        if (!results.getChildren().isEmpty()) {
+            for (Object child : results.getChildren()) {
+                report((Results) child);
+            }
+            return;
+        }
+
+        for (int priority = 1; priority <= 3; priority++) {
+            for (Object o : results.getViolationsWithPriority(priority)) {
+                Violation v = (Violation) o;
+                Formatter formatter = new Formatter();
+                formatter.format("%s:%s%n", results.getPath(), v.getLineNumber());
+                formatter.format("%s: %s", v.getRule().getName(), v.getMessage());
+                LOGGER.error(formatter.toString());
+            }
+        }
+    }
+
+}
diff --git a/subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/code-quality.properties b/subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/code-quality.properties
index cb7c257..d23cb61 100644
--- a/subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/code-quality.properties
+++ b/subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/code-quality.properties
@@ -1,16 +1 @@
-#
-# Copyright 2010 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-implementation-class=org.gradle.api.plugins.quality.CodeQualityPlugin
+implementation-class=org.gradle.api.plugins.quality.CodeQualityPlugin
diff --git a/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/CodeQualityPluginTest.groovy b/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/CodeQualityPluginTest.groovy
index 2479b54..c454446 100644
--- a/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/CodeQualityPluginTest.groovy
+++ b/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/CodeQualityPluginTest.groovy
@@ -94,6 +94,7 @@ class CodeQualityPluginTest {
         assertThat(task, instanceOf(CodeNarc))
         assertThat(task.defaultSource, equalTo(project.sourceSets.test.allGroovy))
         assertThat(task.configFile, equalTo(project.codeNarcConfigFile))
+        assertThat(task.reportFormat, equalTo(project.codeNarcReportsFormat))
         assertThat(task.reportFile, equalTo(project.file("build/reports/codenarc/test.html")))
         assertThat(task, dependsOn())
 
@@ -102,6 +103,7 @@ class CodeQualityPluginTest {
         assertThat(task, instanceOf(CodeNarc))
         assertThat(task.defaultSource, equalTo(project.sourceSets.custom.allGroovy))
         assertThat(task.configFile, equalTo(project.codeNarcConfigFile))
+        assertThat(task.reportFormat, equalTo(project.codeNarcReportsFormat))
         assertThat(task.reportFile, equalTo(project.file("build/reports/codenarc/custom.html")))
         assertThat(task, dependsOn())
 
diff --git a/subprojects/core-impl/core-impl.gradle b/subprojects/core-impl/core-impl.gradle
new file mode 100644
index 0000000..b810424
--- /dev/null
+++ b/subprojects/core-impl/core-impl.gradle
@@ -0,0 +1,17 @@
+apply plugin: "groovy"
+
+dependencies {
+    groovy libraries.groovy_depends
+
+    compile project(":core")
+    compile libraries.guava
+    compile libraries.commons_lang
+    compile libraries.ivy
+    compile "org.apache.maven:maven-ant-tasks:2.1.1 at jar"
+    compile "org.sonatype.pmaven:pmaven-common:0.8-20100325 at jar"
+    compile "org.sonatype.pmaven:pmaven-groovy:0.8-20100325 at jar"
+    compile "org.codehaus.plexus:plexus-component-annotations:1.5.2 at jar"
+
+    testCompile libraries.junit
+    testCompile project(path: ':core', configuration: 'testFixtures')
+}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultDependencyManagementServices.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultDependencyManagementServices.java
new file mode 100644
index 0000000..7f1b6fe
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultDependencyManagementServices.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.api.artifacts.maven.MavenFactory;
+import org.gradle.api.internal.artifacts.ivyservice.ResolverFactory;
+import org.gradle.api.internal.artifacts.publish.maven.DefaultLocalMavenCacheLocator;
+import org.gradle.api.internal.artifacts.publish.maven.DefaultMavenFactory;
+import org.gradle.api.internal.artifacts.repositories.DefaultResolverFactory;
+import org.gradle.api.internal.project.DefaultServiceRegistry;
+import org.gradle.api.internal.project.ServiceRegistry;
+import org.gradle.logging.LoggingManagerInternal;
+
+public class DefaultDependencyManagementServices extends DefaultServiceRegistry implements DependencyManagementServices {
+    public DefaultDependencyManagementServices(ServiceRegistry parent) {
+        super(parent);
+    }
+
+    protected ResolverFactory createResolverFactory() {
+        return new DefaultResolverFactory(getFactory(LoggingManagerInternal.class), get(MavenFactory.class), new DefaultLocalMavenCacheLocator());
+    }
+
+    protected MavenFactory createMavenFactory() {
+        return new DefaultMavenFactory();
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/ArtifactPom.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/ArtifactPom.java
new file mode 100644
index 0000000..f50d3cf
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/ArtifactPom.java
@@ -0,0 +1,41 @@
+/*
+ * 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.apache.ivy.core.module.descriptor.Artifact;
+import org.gradle.api.artifacts.PublishArtifact;
+import org.gradle.api.artifacts.maven.MavenPom;
+
+import java.io.File;
+import java.util.Set;
+
+/**
+ * @author Hans Dockter
+ */
+public interface ArtifactPom {
+    /**
+     * @return The main artifact, may be null.
+     */
+    PublishArtifact getArtifact();
+
+    MavenPom getPom();
+
+    void addArtifact(Artifact artifact, File src);
+
+    Set<PublishArtifact> getAttachedArtifacts();
+
+    PublishArtifact writePom(File pomFile);
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/ArtifactPomContainer.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/ArtifactPomContainer.java
new file mode 100644
index 0000000..d76d77e
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/ArtifactPomContainer.java
@@ -0,0 +1,31 @@
+/*
+ * 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.apache.ivy.core.module.descriptor.Artifact;
+import org.gradle.api.artifacts.maven.MavenDeployment;
+
+import java.io.File;
+import java.util.Set;
+
+/**
+ * @author Hans Dockter
+ */
+public interface ArtifactPomContainer {
+    void addArtifact(Artifact artifact, File src);
+
+    Set<MavenDeployment> createDeployableFilesInfos();
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/ArtifactPomFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/ArtifactPomFactory.java
new file mode 100644
index 0000000..b78f654
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/ArtifactPomFactory.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.api.internal.artifacts.publish.maven;
+
+import org.gradle.api.artifacts.maven.MavenPom;
+
+/**
+ * @author Hans Dockter
+ */
+public interface ArtifactPomFactory {
+    ArtifactPom createArtifactPom(MavenPom pom);
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultArtifactPomFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultArtifactPomFactory.java
new file mode 100644
index 0000000..6429118
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultArtifactPomFactory.java
@@ -0,0 +1,28 @@
+/*
+ * 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;
+import org.gradle.api.internal.artifacts.publish.maven.deploy.DefaultArtifactPom;
+
+/**
+ * @author Hans Dockter
+ */
+public class DefaultArtifactPomFactory implements ArtifactPomFactory {
+    public ArtifactPom createArtifactPom(MavenPom pom) {
+        return new DefaultArtifactPom(pom);
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultLocalMavenCacheLocator.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultLocalMavenCacheLocator.java
new file mode 100644
index 0000000..bf27eed
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultLocalMavenCacheLocator.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.apache.maven.settings.DefaultMavenSettingsBuilder;
+import org.apache.maven.settings.MavenSettingsBuilder;
+import org.apache.maven.settings.Settings;
+import org.gradle.api.internal.artifacts.publish.maven.deploy.mvnsettings.MavenSettingsProvider;
+import org.gradle.api.internal.artifacts.publish.maven.pombuilder.PlexusLoggerAdapter;
+import org.gradle.util.UncheckedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.lang.reflect.Field;
+
+/**
+ * @author Steve Ebersole
+ */
+public class DefaultLocalMavenCacheLocator implements LocalMavenCacheLocator {
+    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultLocalMavenCacheLocator.class);
+    private static final String USER_HOME_MARKER = "${user.home}/";
+
+    public File getLocalMavenCache() {
+        MavenSettingsProvider mavenSettingsProvider = new MavenSettingsProvider();
+        File userHome = new File(System.getProperty("user.home"));
+        File userSettings = mavenSettingsProvider.getUserSettingsFile();
+
+        if (userSettings.exists()) {
+            File overriddenMavenLocal = extractMavenLocal(userSettings, userHome);
+            if (overriddenMavenLocal != null) {
+                return overriddenMavenLocal;
+            }
+        }
+
+        return mavenSettingsProvider.getLocalMavenRepository();
+    }
+
+    private File extractMavenLocal(File userSettings, File userHome) {
+        Settings settings = extractSettings(userSettings);
+        String override = settings.getLocalRepository();
+        if (override != null) {
+            override = override.trim();
+            if (override.length() > 0) {
+                // Nice, it does not even handle the interpolation for us, so we'll handle some common cases...
+                if (override.startsWith(USER_HOME_MARKER)) {
+                    override = userHome.getAbsolutePath() + '/' + override.substring(USER_HOME_MARKER.length());
+                }
+                return new File(override);
+            }
+        }
+        return null;
+    }
+
+    private Settings extractSettings(File userSettings) {
+        try {
+            MavenSettingsBuilder builder = buildSettingsBuilder(userSettings);
+            return builder.buildSettings();
+        } catch (Exception e) {
+            throw UncheckedException.asUncheckedException(e);
+        }
+    }
+
+    private MavenSettingsBuilder buildSettingsBuilder(File userSettings) throws Exception {
+        final String userSettingsPath = userSettings.getAbsolutePath();
+
+        DefaultMavenSettingsBuilder builder = new DefaultMavenSettingsBuilder();
+        builder.enableLogging(new PlexusLoggerAdapter(LOGGER));
+
+        Field userSettingsPathField = DefaultMavenSettingsBuilder.class.getDeclaredField("userSettingsPath");
+        userSettingsPathField.setAccessible(true);
+        userSettingsPathField.set(builder, userSettingsPath);
+
+        Field globalSettingsPathField = DefaultMavenSettingsBuilder.class.getDeclaredField("globalSettingsPath");
+        globalSettingsPathField.setAccessible(true);
+        globalSettingsPathField.set(builder, userSettingsPath);
+
+        builder.initialize();
+
+        return builder;
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenFactory.java
new file mode 100644
index 0000000..8887b20
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenFactory.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.*;
+import org.gradle.api.internal.Factory;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.artifacts.ConfigurationContainer;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.internal.artifacts.publish.maven.dependencies.DefaultPomDependenciesConverter;
+import org.gradle.api.internal.artifacts.publish.maven.dependencies.DefaultExcludeRuleConverter;
+import org.gradle.api.internal.artifacts.publish.maven.dependencies.DefaultConf2ScopeMappingContainer;
+
+import java.util.Map;
+
+public class DefaultMavenFactory implements MavenFactory {
+
+    public Factory<MavenPom> createMavenPomFactory(ConfigurationContainer configurationContainer, Conf2ScopeMappingContainer conf2ScopeMappingContainer, FileResolver fileResolver) {
+        return new DefaultMavenPomFactory(configurationContainer, conf2ScopeMappingContainer, createPomDependenciesConverter(), fileResolver);
+    }
+
+    public Factory<MavenPom> createMavenPomFactory(ConfigurationContainer configurationContainer, Map<Configuration, Conf2ScopeMapping> mappings, FileResolver fileResolver) {
+        return new DefaultMavenPomFactory(configurationContainer, createConf2ScopeMappingContainer(mappings), createPomDependenciesConverter(), fileResolver);
+    }
+
+    private PomDependenciesConverter createPomDependenciesConverter() {
+        return new DefaultPomDependenciesConverter(new DefaultExcludeRuleConverter());
+    }
+
+    public Conf2ScopeMappingContainer createConf2ScopeMappingContainer(Map<Configuration, Conf2ScopeMapping> mappings) {
+        return new DefaultConf2ScopeMappingContainer(mappings);
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPom.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPom.java
new file mode 100644
index 0000000..cee4a60
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPom.java
@@ -0,0 +1,252 @@
+/*
+ * 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 groovy.lang.Closure;
+import org.apache.commons.io.IOUtils;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Model;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.gradle.api.Action;
+import org.gradle.api.UncheckedIOException;
+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.pombuilder.CustomModelBuilder;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.listener.ActionBroadcast;
+
+import java.io.*;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author Hans Dockter
+ */
+public class DefaultMavenPom implements MavenPom {
+    private PomDependenciesConverter pomDependenciesConverter;
+    private FileResolver fileResolver;
+    private MavenProject mavenProject = new MavenProject();
+    private Conf2ScopeMappingContainer scopeMappings;
+    private ActionBroadcast<MavenPom> whenConfiguredActions = new ActionBroadcast<MavenPom>();
+    private XmlTransformer withXmlActions = new XmlTransformer();
+    private ConfigurationContainer configurations;
+
+    public DefaultMavenPom(ConfigurationContainer configurationContainer, Conf2ScopeMappingContainer scopeMappings, PomDependenciesConverter pomDependenciesConverter,
+                           FileResolver fileResolver) {
+        this.configurations = configurationContainer;
+        this.scopeMappings = scopeMappings;
+        this.pomDependenciesConverter = pomDependenciesConverter;
+        this.fileResolver = fileResolver;
+        mavenProject.setModelVersion("4.0.0");
+    }
+
+    public Conf2ScopeMappingContainer getScopeMappings() {
+        return scopeMappings;
+    }
+
+    public ConfigurationContainer getConfigurations() {
+        return configurations;
+    }
+
+    public DefaultMavenPom setConfigurations(ConfigurationContainer configurations) {
+        this.configurations = configurations;
+        return this;
+    }
+    
+    public DefaultMavenPom setGroupId(String groupId) {
+        getModel().setGroupId(groupId);
+        return this;
+    }
+
+    public String getGroupId() {
+        return getModel().getGroupId();
+    }
+
+    public DefaultMavenPom setArtifactId(String artifactId) {
+        getModel().setArtifactId(artifactId);
+        return this;
+    }
+
+    public String getArtifactId() {
+        return getModel().getArtifactId();
+    }
+
+    @SuppressWarnings("unchecked")
+    public DefaultMavenPom setDependencies(List<?> dependencies) {
+        getModel().setDependencies((List<Dependency>) dependencies);
+        return this;
+    }
+
+    public List<Dependency> getDependencies() {
+        return getModel().getDependencies();
+    }
+
+    public DefaultMavenPom setName(String name) {
+        getModel().setName(name);
+        return this;
+    }
+
+    public String getName() {
+        return getModel().getName();
+    }
+
+    public DefaultMavenPom setVersion(String version) {
+        getModel().setVersion(version);
+        return this;
+    }
+
+    public String getVersion() {
+        return getModel().getVersion();
+    }
+
+    public String getPackaging() {
+        return getModel().getPackaging();
+    }
+
+    public DefaultMavenPom setPackaging(String packaging) {
+        getModel().setPackaging(packaging);
+        return this;
+    }
+
+    public DefaultMavenPom project(Closure cl) {
+        CustomModelBuilder pomBuilder = new CustomModelBuilder(getModel());
+        InvokerHelper.invokeMethod(pomBuilder, "project", cl);
+        return this;
+    }
+
+    public Model getModel() {
+        return mavenProject.getModel();
+    }
+
+    public DefaultMavenPom setModel(Object model) {
+        this.mavenProject = new MavenProject((Model) model);
+        return this;
+    }
+
+    public MavenProject getMavenProject() {
+        return mavenProject;
+    }
+
+    public DefaultMavenPom setMavenProject(MavenProject mavenProject) {
+        this.mavenProject = mavenProject;
+        return this;
+    }
+
+    @SuppressWarnings("unchecked")
+    public List<Dependency> getGeneratedDependencies() {
+        if (configurations == null) {
+            return Collections.emptyList();
+        }
+        return (List<Dependency>) pomDependenciesConverter.convert(getScopeMappings(), configurations.getAll());
+    }
+
+    public DefaultMavenPom getEffectivePom() {
+        DefaultMavenPom effectivePom = new DefaultMavenPom(null, this.scopeMappings, pomDependenciesConverter, fileResolver);
+        try {
+            effectivePom.setMavenProject((MavenProject) mavenProject.clone());
+        } catch (CloneNotSupportedException e) {
+            throw new RuntimeException(e);
+        }
+        effectivePom.getDependencies().addAll(getGeneratedDependencies());
+        effectivePom.withXmlActions = withXmlActions;
+        whenConfiguredActions.execute(effectivePom);
+        return effectivePom;
+    }
+
+    public PomDependenciesConverter getPomDependenciesConverter() {
+        return pomDependenciesConverter;
+    }
+
+    public FileResolver getFileResolver() {
+        return fileResolver;
+    }
+
+    public DefaultMavenPom setFileResolver(FileResolver fileResolver) {
+        this.fileResolver = fileResolver;
+        return this;
+    }
+
+    public DefaultMavenPom writeTo(final Writer pomWriter) {
+        getEffectivePom().writeNonEffectivePom(pomWriter);
+        return this;
+    }
+
+    public DefaultMavenPom writeTo(Object path) {
+        OutputStream stream = null;
+
+        try {
+            File file = fileResolver.resolve(path);
+            if (file.getParentFile() != null) {
+                file.getParentFile().mkdirs();
+            }
+            stream = new FileOutputStream(file);
+            getEffectivePom().writeNonEffectivePom(stream);
+            return this;
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        } finally {
+            IOUtils.closeQuietly(stream);
+        }
+    }
+
+    private void writeNonEffectivePom(final Writer pomWriter) {
+        try {
+            final StringWriter stringWriter = new StringWriter();
+            mavenProject.writeModel(stringWriter);
+            withXmlActions.transform(stringWriter.toString(), pomWriter);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        } finally {
+            IOUtils.closeQuietly(pomWriter);
+        }
+    }
+
+    private void writeNonEffectivePom(OutputStream stream) {
+        try {
+            final StringWriter stringWriter = new StringWriter();
+            mavenProject.writeModel(stringWriter);
+            withXmlActions.transform(stringWriter.toString(), stream);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        } finally {
+            IOUtils.closeQuietly(stream);
+        }
+    }
+
+    public DefaultMavenPom whenConfigured(final Closure closure) {
+        whenConfiguredActions.add(closure);
+        return this;
+    }
+
+    public DefaultMavenPom whenConfigured(final Action<MavenPom> action) {
+        whenConfiguredActions.add(action);
+        return this;
+    }
+
+    public DefaultMavenPom withXml(final Closure closure) {
+        withXmlActions.addAction(closure);
+        return this;
+    }
+
+    public DefaultMavenPom withXml(final Action<XmlProvider> action) {
+        withXmlActions.addAction(action);
+        return this;
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomFactory.java
new file mode 100644
index 0000000..ba27909
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomFactory.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.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.file.FileResolver;
+
+/**
+ * @author Hans Dockter
+ */
+public class DefaultMavenPomFactory implements Factory<MavenPom> {
+    private ConfigurationContainer configurationContainer;
+    private Conf2ScopeMappingContainer conf2ScopeMappingContainer;
+    private PomDependenciesConverter pomDependenciesConverter;
+    private FileResolver fileResolver;
+
+
+    public DefaultMavenPomFactory(ConfigurationContainer configurationContainer, Conf2ScopeMappingContainer conf2ScopeMappingContainer, PomDependenciesConverter pomDependenciesConverter,
+                                  FileResolver fileResolver) {
+        this.configurationContainer = configurationContainer;
+        this.conf2ScopeMappingContainer = conf2ScopeMappingContainer;
+        this.pomDependenciesConverter = pomDependenciesConverter;
+        this.fileResolver = fileResolver;
+    }
+
+    public MavenPom create() {
+        return new DefaultMavenPom(configurationContainer,
+                new DefaultConf2ScopeMappingContainer(conf2ScopeMappingContainer.getMappings()), pomDependenciesConverter, fileResolver);
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/ExcludeRuleConverter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/ExcludeRuleConverter.java
new file mode 100644
index 0000000..2843dda
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/ExcludeRuleConverter.java
@@ -0,0 +1,26 @@
+/*
+ * 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.ExcludeRule;
+
+
+/**
+ * @author Hans Dockter
+ */
+public interface ExcludeRuleConverter {
+    Object convert(ExcludeRule excludeRule);
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/LocalMavenCacheLocator.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/LocalMavenCacheLocator.java
new file mode 100644
index 0000000..be5ea95
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/LocalMavenCacheLocator.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.api.internal.artifacts.publish.maven;
+
+import java.io.File;
+
+public interface LocalMavenCacheLocator {
+    File getLocalMavenCache();
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/PomDependenciesConverter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/PomDependenciesConverter.java
new file mode 100644
index 0000000..5a71a17
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/PomDependenciesConverter.java
@@ -0,0 +1,29 @@
+/*
+ * 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.Configuration;
+import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author Hans Dockter
+ */
+public interface PomDependenciesConverter {
+    public List<?> convert(Conf2ScopeMappingContainer conf2ScopeMappingContainer, Set<Configuration> configurations);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultConf2ScopeMappingContainer.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultConf2ScopeMappingContainer.java
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultConf2ScopeMappingContainer.java
rename to subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultConf2ScopeMappingContainer.java
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultExcludeRuleConverter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultExcludeRuleConverter.java
new file mode 100644
index 0000000..3494aef
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultExcludeRuleConverter.java
@@ -0,0 +1,40 @@
+/*
+ * 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.dependencies;
+
+import org.apache.maven.model.Exclusion;
+import org.gradle.api.artifacts.ExcludeRule;
+import org.gradle.api.internal.artifacts.publish.maven.ExcludeRuleConverter;
+
+
+/**
+ * @author Hans Dockter
+ */
+public class DefaultExcludeRuleConverter implements ExcludeRuleConverter {
+    public Exclusion convert(ExcludeRule excludeRule) {
+        if (isConvertable(excludeRule)) {
+            Exclusion exclusion = new Exclusion();
+            exclusion.setGroupId(excludeRule.getExcludeArgs().get(ExcludeRule.GROUP_KEY));
+            exclusion.setArtifactId(excludeRule.getExcludeArgs().get(ExcludeRule.MODULE_KEY));
+            return exclusion;
+        }
+        return null;
+    }
+
+    private boolean isConvertable(ExcludeRule excludeRule) {
+        return excludeRule.getExcludeArgs().containsKey(ExcludeRule.GROUP_KEY) && excludeRule.getExcludeArgs().containsKey(ExcludeRule.MODULE_KEY);
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultPomDependenciesConverter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultPomDependenciesConverter.java
new file mode 100644
index 0000000..863f738
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultPomDependenciesConverter.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dependencies;
+
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Exclusion;
+import org.gradle.api.GradleException;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.DependencyArtifact;
+import org.gradle.api.artifacts.ExcludeRule;
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.artifacts.maven.Conf2ScopeMapping;
+import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
+import org.gradle.api.internal.artifacts.publish.maven.ExcludeRuleConverter;
+import org.gradle.api.internal.artifacts.publish.maven.PomDependenciesConverter;
+
+import java.util.*;
+
+/**
+ * @author Hans Dockter
+ */
+public class DefaultPomDependenciesConverter implements PomDependenciesConverter {
+    private ExcludeRuleConverter excludeRuleConverter;
+
+    public DefaultPomDependenciesConverter(ExcludeRuleConverter excludeRuleConverter) {
+        this.excludeRuleConverter = excludeRuleConverter;
+    }
+
+    public List<org.apache.maven.model.Dependency> convert(Conf2ScopeMappingContainer conf2ScopeMappingContainer, Set<Configuration> configurations) {
+        Map<ModuleDependency, Set<Configuration>> dependencyToConfigurations = createDependencyToConfigurationsMap(configurations);
+        Map<ModuleDependency, String> dependenciesMap = createDependencyToScopeMap(conf2ScopeMappingContainer, dependencyToConfigurations);
+        List<org.apache.maven.model.Dependency> mavenDependencies = new ArrayList<org.apache.maven.model.Dependency>();
+        for (ModuleDependency dependency : dependenciesMap.keySet()) {
+            if (dependency.getArtifacts().size() == 0) {
+                addFromDependencyDescriptor(mavenDependencies, dependency, dependenciesMap.get(dependency), dependencyToConfigurations.get(dependency));
+            } else {
+                addFromArtifactDescriptor(mavenDependencies, dependency, dependenciesMap.get(dependency), dependencyToConfigurations.get(dependency));
+            }
+        }
+        return mavenDependencies;
+    }
+    
+    private Map<ModuleDependency, String> createDependencyToScopeMap(Conf2ScopeMappingContainer conf2ScopeMappingContainer, 
+            Map<ModuleDependency, Set<Configuration>> dependencyToConfigurations) {
+        Map<ModuleDependency, String> dependencyToScope = new HashMap<ModuleDependency, String>();
+        for (ModuleDependency dependency : dependencyToConfigurations.keySet()) {
+            Conf2ScopeMapping conf2ScopeDependencyMapping = conf2ScopeMappingContainer.getMapping(dependencyToConfigurations.get(dependency));
+            if (!useScope(conf2ScopeMappingContainer, conf2ScopeDependencyMapping)) {
+                continue;
+            }
+            dependencyToScope.put(findDependency(dependency, conf2ScopeDependencyMapping.getConfiguration()),
+                    conf2ScopeDependencyMapping.getScope());
+        }
+        return dependencyToScope;
+    }
+
+    private ModuleDependency findDependency(ModuleDependency dependency, Configuration configuration) {
+        for (ModuleDependency configurationDependency : configuration.getDependencies(ModuleDependency.class)) {
+            if (dependency.equals(configurationDependency)) {
+                return configurationDependency;
+            }
+        }
+        throw new GradleException("Dependency could not be found. We should never get here!");
+    }
+
+    private boolean useScope(Conf2ScopeMappingContainer conf2ScopeMappingContainer, Conf2ScopeMapping conf2ScopeMapping) {
+        return conf2ScopeMapping.getScope() != null || !conf2ScopeMappingContainer.isSkipUnmappedConfs();
+    }
+
+    private Map<ModuleDependency, Set<Configuration>> createDependencyToConfigurationsMap(Set<Configuration> configurations) {
+        Map<ModuleDependency, Set<Configuration>> dependencySetMap = new HashMap<ModuleDependency, Set<Configuration>>();
+        for (Configuration configuration : configurations) {
+            for (ModuleDependency dependency : configuration.getDependencies(ModuleDependency.class)) {
+                if (dependencySetMap.get(dependency) == null) {
+                    dependencySetMap.put(dependency, new HashSet<Configuration>());
+                }
+                dependencySetMap.get(dependency).add(configuration);
+            }
+        }
+        return dependencySetMap;
+    }
+
+    private void addFromArtifactDescriptor(List<Dependency> mavenDependencies, ModuleDependency dependency, String scope, 
+            Set<Configuration> configurations) {
+        for (DependencyArtifact artifact : dependency.getArtifacts()) {
+            mavenDependencies.add(createMavenDependencyFromArtifactDescriptor(dependency, artifact, scope, configurations));
+        }
+    }
+
+    private void addFromDependencyDescriptor(List<Dependency> mavenDependencies, ModuleDependency dependency, String scope, 
+            Set<Configuration> configurations) {
+        mavenDependencies.add(createMavenDependencyFromDependencyDescriptor(dependency, scope, configurations));
+    }
+
+    private Dependency createMavenDependencyFromArtifactDescriptor(ModuleDependency dependency, DependencyArtifact artifact, String scope,
+            Set<Configuration> configurations) {
+        return createMavenDependency(dependency, artifact.getName(), artifact.getType(), scope, artifact.getClassifier(), configurations);
+    }
+
+    private Dependency createMavenDependencyFromDependencyDescriptor(ModuleDependency dependency, String scope, Set<Configuration> configurations) {
+        return createMavenDependency(dependency, dependency.getName(), null, scope, null, configurations);
+    }
+
+    private Dependency createMavenDependency(ModuleDependency dependency, String name, String type, String scope, String classifier,
+            Set<Configuration> configurations) {
+        Dependency mavenDependency =  new Dependency();
+        mavenDependency.setGroupId(dependency.getGroup());
+        mavenDependency.setArtifactId(name);
+        mavenDependency.setVersion(dependency.getVersion());
+        mavenDependency.setType(type);
+        mavenDependency.setScope(scope);
+        mavenDependency.setOptional(false);
+        mavenDependency.setClassifier(classifier);
+        mavenDependency.setExclusions(getExclusions(dependency, configurations));
+        return mavenDependency;
+    }
+
+    private List<Exclusion> getExclusions(ModuleDependency dependency, Set<Configuration> configurations) {
+        List<Exclusion> mavenExclusions = new ArrayList<Exclusion>();
+        Set<ExcludeRule> excludeRules = new HashSet<ExcludeRule>(dependency.getExcludeRules());
+        for (Configuration configuration : configurations) {
+            excludeRules.addAll(configuration.getExcludeRules());
+        }
+        for (ExcludeRule excludeRule : excludeRules) {
+            Exclusion mavenExclusion = (Exclusion) excludeRuleConverter.convert(excludeRule);
+            if (mavenExclusion != null) {
+                mavenExclusions.add(mavenExclusion);
+            }
+        }
+        return mavenExclusions;
+    }
+
+    public ExcludeRuleConverter getExcludeRuleConverter() {
+        return excludeRuleConverter;
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/AbstractMavenResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/AbstractMavenResolver.java
new file mode 100644
index 0000000..edb830e
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/AbstractMavenResolver.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+import groovy.lang.Closure;
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.cache.RepositoryCacheManager;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.report.DownloadReport;
+import org.apache.ivy.core.resolve.DownloadOptions;
+import org.apache.ivy.core.resolve.ResolveData;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.core.search.ModuleEntry;
+import org.apache.ivy.core.search.OrganisationEntry;
+import org.apache.ivy.core.search.RevisionEntry;
+import org.apache.ivy.plugins.namespace.Namespace;
+import org.apache.ivy.plugins.resolver.ResolverSettings;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+import org.apache.maven.artifact.ant.AttachedArtifact;
+import org.apache.maven.artifact.ant.InstallDeployTaskSupport;
+import org.apache.maven.artifact.ant.Pom;
+import org.apache.maven.settings.Settings;
+import org.apache.tools.ant.Project;
+import org.gradle.api.Action;
+import org.gradle.api.artifacts.PublishArtifact;
+import org.gradle.api.artifacts.maven.*;
+import org.gradle.api.internal.artifacts.ivyservice.NoOpRepositoryCacheManager;
+import org.gradle.api.internal.artifacts.publish.maven.ArtifactPomContainer;
+import org.gradle.api.internal.artifacts.publish.maven.deploy.mvnsettings.EmptyMavenSettingsSupplier;
+import org.gradle.api.internal.artifacts.publish.maven.deploy.mvnsettings.MavenSettingsSupplier;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.listener.ActionBroadcast;
+import org.gradle.logging.LoggingManagerInternal;
+import org.gradle.util.AntUtil;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Hans Dockter
+ */
+public abstract class AbstractMavenResolver implements MavenResolver {
+
+    private String name;
+    
+    private ArtifactPomContainer artifactPomContainer;
+
+    private PomFilterContainer pomFilterContainer;
+
+    private Settings settings;
+
+    private LoggingManagerInternal loggingManager;
+
+    private final ActionBroadcast<MavenDeployment> beforeDeploymentActions = new ActionBroadcast<MavenDeployment>();
+
+    protected MavenSettingsSupplier mavenSettingsSupplier = new EmptyMavenSettingsSupplier();
+
+    public AbstractMavenResolver(String name, PomFilterContainer pomFilterContainer, ArtifactPomContainer artifactPomContainer, LoggingManagerInternal loggingManager) {
+        this.name = name;
+        this.pomFilterContainer = pomFilterContainer;
+        this.artifactPomContainer = artifactPomContainer;
+        this.loggingManager = loggingManager;
+    }
+
+    protected abstract InstallDeployTaskSupport createPreConfiguredTask(Project project);
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+    
+    public ResolvedModuleRevision getDependency(DependencyDescriptor dd, ResolveData data) throws ParseException {
+        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
+    }
+
+    public ResolvedResource findIvyFileRef(DependencyDescriptor dd, ResolveData data) {
+        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
+    }
+
+    public DownloadReport download(Artifact[] artifacts, DownloadOptions options) {
+        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
+    }
+
+    public ArtifactDownloadReport download(ArtifactOrigin artifact, DownloadOptions options) {
+        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
+    }
+
+    public boolean exists(Artifact artifact) {
+        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
+    }
+
+    public ArtifactOrigin locate(Artifact artifact) {
+        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
+    }
+
+    public void reportFailure() {
+        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
+    }
+
+    public void reportFailure(Artifact art) {
+        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
+    }
+
+    public String[] listTokenValues(String token, Map otherTokenValues) {
+        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
+    }
+
+    public Map[] listTokenValues(String[] tokens, Map criteria) {
+        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
+    }
+
+    public OrganisationEntry[] listOrganisations() {
+        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
+    }
+
+    public ModuleEntry[] listModules(OrganisationEntry org) {
+        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
+    }
+
+    public RevisionEntry[] listRevisions(ModuleEntry module) {
+        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
+    }
+
+    public Namespace getNamespace() {
+        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
+    }
+
+    public void dumpSettings() {
+        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
+    }
+
+
+    public void publish(Artifact artifact, File src, boolean overwrite) throws IOException {
+        if (isIgnorable(artifact)) {
+            return;
+        }
+        getArtifactPomContainer().addArtifact(artifact, src);
+    }
+
+    private boolean isIgnorable(Artifact artifact) {
+        return artifact.getType().equals("ivy");
+    }
+
+    public void beginPublishTransaction(ModuleRevisionId module, boolean overwrite) throws IOException {
+        // do nothing
+    }
+
+    public void abortPublishTransaction() throws IOException {
+        // do nothing
+    }
+
+    public void commitPublishTransaction() throws IOException {
+        InstallDeployTaskSupport installDeployTaskSupport = createPreConfiguredTask(AntUtil.createProject());
+        Set<MavenDeployment> mavenDeployments = getArtifactPomContainer().createDeployableFilesInfos();
+        mavenSettingsSupplier.supply(installDeployTaskSupport);
+        for (MavenDeployment mavenDeployment : mavenDeployments) {
+            beforeDeploymentActions.execute(mavenDeployment);
+            addPomAndArtifact(installDeployTaskSupport, mavenDeployment);
+            execute(installDeployTaskSupport);
+        }
+        mavenSettingsSupplier.done();
+        settings = ((CustomInstallDeployTaskSupport) installDeployTaskSupport).getSettings();
+    }
+
+    private void execute(InstallDeployTaskSupport deployTask) {
+        loggingManager.captureStandardOutput(LogLevel.INFO).start();
+        try {
+            deployTask.execute();
+        } finally {
+            loggingManager.stop();
+        }
+    }
+
+    private void addPomAndArtifact(InstallDeployTaskSupport installOrDeployTask, MavenDeployment mavenDeployment) {
+        Pom pom = new Pom();
+        pom.setProject(installOrDeployTask.getProject());
+        pom.setFile(mavenDeployment.getPomArtifact().getFile());
+        installOrDeployTask.addPom(pom);
+        if (mavenDeployment.getMainArtifact() != null) {
+            installOrDeployTask.setFile(mavenDeployment.getMainArtifact().getFile());
+        }
+        for (PublishArtifact classifierArtifact : mavenDeployment.getAttachedArtifacts()) {
+            AttachedArtifact attachedArtifact = installOrDeployTask.createAttach();
+            attachedArtifact.setClassifier(classifierArtifact.getClassifier());
+            attachedArtifact.setFile(classifierArtifact.getFile());
+            attachedArtifact.setType(classifierArtifact.getType());
+        }
+    }
+
+    public void setSettings(ResolverSettings settings) {
+        // do nothing
+    }
+
+    public RepositoryCacheManager getRepositoryCacheManager() {
+        return new NoOpRepositoryCacheManager(getName());
+    }
+
+    public ArtifactPomContainer getArtifactPomContainer() {
+        return artifactPomContainer;
+    }
+
+    public void setArtifactPomContainer(ArtifactPomContainer artifactPomContainer) {
+        this.artifactPomContainer = artifactPomContainer;
+    }
+
+    public Settings getSettings() {
+        return settings;
+    }
+
+    public PublishFilter getFilter() {
+        return pomFilterContainer.getFilter();
+    }
+
+    public void setFilter(PublishFilter defaultFilter) {
+        pomFilterContainer.setFilter(defaultFilter);
+    }
+
+    public MavenPom getPom() {
+        return pomFilterContainer.getPom();
+    }
+
+    public void setPom(MavenPom defaultPom) {
+        pomFilterContainer.setPom(defaultPom);
+    }
+
+    public MavenPom addFilter(String name, PublishFilter publishFilter) {
+        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);
+    }
+
+    public MavenPom pom(String name) {
+        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();
+    }
+
+    public PomFilterContainer getPomFilterContainer() {
+        return pomFilterContainer;
+    }
+
+    public void setPomFilterContainer(PomFilterContainer pomFilterContainer) {
+        this.pomFilterContainer = pomFilterContainer;
+    }
+
+    public void beforeDeployment(Action<? super MavenDeployment> action) {
+        beforeDeploymentActions.add(action);
+    }
+
+    public void beforeDeployment(Closure action) {
+        beforeDeploymentActions.add(action);
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenDeployer.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenDeployer.java
new file mode 100644
index 0000000..7dac95f
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenDeployer.java
@@ -0,0 +1,129 @@
+/*
+ * 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;
+
+import org.apache.maven.artifact.ant.DeployTask;
+import org.apache.maven.artifact.ant.InstallDeployTaskSupport;
+import org.apache.maven.artifact.ant.RemoteRepository;
+import org.apache.tools.ant.Project;
+import org.codehaus.plexus.PlexusContainer;
+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.api.internal.artifacts.publish.maven.ArtifactPomContainer;
+import org.gradle.logging.LoggingManagerInternal;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author Hans Dockter
+ */
+public class BaseMavenDeployer extends AbstractMavenResolver implements MavenDeployer {
+    private RemoteRepository remoteRepository;
+
+    private RemoteRepository remoteSnapshotRepository;
+
+    private Factory<CustomDeployTask> deployTaskFactory = new DefaultDeployTaskFactory();
+
+    private Configuration configuration;
+
+    // todo remove this property once configuration can handle normal file system dependencies
+    private List<File> protocolProviderJars = new ArrayList<File>();
+
+    private boolean uniqueVersion = true;
+
+    public BaseMavenDeployer(String name, PomFilterContainer pomFilterContainer, ArtifactPomContainer artifactPomContainer, LoggingManagerInternal loggingManager) {
+        super(name, pomFilterContainer, artifactPomContainer, loggingManager);
+    }
+
+    protected InstallDeployTaskSupport createPreConfiguredTask(Project project) {
+        CustomDeployTask deployTask = deployTaskFactory.create();
+        deployTask.setProject(project);
+        deployTask.setUniqueVersion(isUniqueVersion());
+        addProtocolProvider(deployTask);
+        addRemoteRepositories(deployTask);
+        return deployTask;
+    }
+
+    private void addProtocolProvider(CustomDeployTask deployTask) {
+        PlexusContainer plexusContainer = deployTask.getContainer();
+        for (File wagonProviderJar : getJars()) {
+            try {
+                plexusContainer.addJarResource(wagonProviderJar);
+            } catch (PlexusContainerException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    private List<File> getJars() {
+        return configuration != null ? new ArrayList(configuration.resolve()) : protocolProviderJars;
+    }
+
+    private void addRemoteRepositories(DeployTask deployTask) {
+        deployTask.addRemoteRepository(remoteRepository);
+        deployTask.addRemoteSnapshotRepository(remoteSnapshotRepository);
+    }
+
+    public RemoteRepository getRepository() {
+        return remoteRepository;
+    }
+
+    public void setRepository(Object remoteRepository) {
+        this.remoteRepository = (RemoteRepository) remoteRepository;
+    }
+
+    public RemoteRepository getSnapshotRepository() {
+        return remoteSnapshotRepository;
+    }
+
+    public void setSnapshotRepository(Object remoteSnapshotRepository) {
+        this.remoteSnapshotRepository = (RemoteRepository) remoteSnapshotRepository;
+    }
+
+    public Factory<CustomDeployTask> getDeployTaskFactory() {
+        return deployTaskFactory;
+    }
+
+    public void setDeployTaskFactory(Factory<CustomDeployTask> deployTaskFactory) {
+        this.deployTaskFactory = deployTaskFactory;
+    }
+
+    public void addProtocolProviderJars(Collection<File> jars) {
+        protocolProviderJars.addAll(jars);
+    }
+
+    public Configuration getConfiguration() {
+        return configuration;
+    }
+
+    public void setConfiguration(Configuration configuration) {
+        this.configuration = configuration;
+    }
+
+    public boolean isUniqueVersion() {
+        return uniqueVersion;
+    }
+
+    public void setUniqueVersion(boolean uniqueVersion) {
+        this.uniqueVersion = uniqueVersion;
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenInstaller.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenInstaller.java
new file mode 100644
index 0000000..f015d38
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenInstaller.java
@@ -0,0 +1,51 @@
+/*
+ * 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;
+
+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.api.internal.artifacts.publish.maven.ArtifactPomContainer;
+import org.gradle.api.internal.artifacts.publish.maven.deploy.mvnsettings.MaybeUserMavenSettingsSupplier;
+import org.gradle.logging.LoggingManagerInternal;
+
+/**
+ * @author Hans Dockter
+ */
+public class BaseMavenInstaller extends AbstractMavenResolver {
+    private Factory<CustomInstallTask> installTaskFactory = new DefaultInstallTaskFactory();
+
+    public BaseMavenInstaller(String name, PomFilterContainer pomFilterContainer, ArtifactPomContainer artifactPomContainer, LoggingManagerInternal loggingManager) {
+        super(name, pomFilterContainer, artifactPomContainer, loggingManager);
+        mavenSettingsSupplier = new MaybeUserMavenSettingsSupplier();
+    }
+
+    protected InstallDeployTaskSupport createPreConfiguredTask(Project project) {
+        InstallTask installTask = installTaskFactory.create();
+        installTask.setProject(project);
+        return installTask;
+    }
+
+    public Factory<CustomInstallTask> getInstallTaskFactory() {
+        return installTaskFactory;
+    }
+
+    public void setInstallTaskFactory(Factory<CustomInstallTask> installTaskFactory) {
+        this.installTaskFactory = installTaskFactory;
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BasePomFilterContainer.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BasePomFilterContainer.java
new file mode 100644
index 0000000..aa84e94
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BasePomFilterContainer.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+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.Factory;
+import org.gradle.util.ConfigureUtil;
+import org.gradle.util.WrapUtil;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Hans Dockter
+ */
+public class BasePomFilterContainer implements PomFilterContainer {
+    private Map<String, PomFilter> pomFilters = new HashMap<String, PomFilter>();
+
+    private PomFilter defaultPomFilter;
+
+    private Factory<MavenPom> mavenPomFactory;
+
+    public BasePomFilterContainer(Factory<MavenPom> mavenPomFactory) {
+        this.mavenPomFactory = mavenPomFactory;
+    }
+
+    public PublishFilter getFilter() {
+        return getDefaultPomFilter().getFilter();
+    }
+
+    public void setFilter(PublishFilter defaultFilter) {
+        getDefaultPomFilter().setFilter(defaultFilter);
+    }
+
+    public MavenPom getPom() {
+        return getDefaultPomFilter().getPomTemplate();
+    }
+
+    public void setPom(MavenPom defaultPom) {
+        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.create();
+        pomFilters.put(name, new DefaultPomFilter(name, pom, publishFilter));
+        return pom;
+    }
+
+    public PublishFilter filter(String name) {
+        if (name == null) {
+            throw new InvalidUserDataException("Name must not be null.");
+        }
+        return pomFilters.get(name).getFilter();
+    }
+
+    public MavenPom pom(String name) {
+        if (name == null) {
+            throw new InvalidUserDataException("Name must not be null.");
+        }
+        return pomFilters.get(name).getPomTemplate();
+    }
+
+    public Iterable<PomFilter> getActivePomFilters() {
+        Iterable<PomFilter> activeArtifactPoms;
+        if (pomFilters.size() == 0 && getDefaultPomFilter() != null) {
+            activeArtifactPoms = WrapUtil.toSet(getDefaultPomFilter());
+        } else {
+            activeArtifactPoms = pomFilters.values();
+        }
+        return activeArtifactPoms;
+    }
+
+    public Factory<MavenPom> getMavenPomFactory() {
+        return mavenPomFactory;
+    }
+
+    public PomFilter getDefaultPomFilter() {
+        if (defaultPomFilter == null) {
+            defaultPomFilter = new DefaultPomFilter(PomFilterContainer.DEFAULT_ARTIFACT_POM_NAME, mavenPomFactory.create(),
+                PublishFilter.ALWAYS_ACCEPT);
+        }
+        return defaultPomFilter;
+    }
+
+    public void setDefaultPomFilter(PomFilter defaultPomFilter) {
+        this.defaultPomFilter = defaultPomFilter;
+    }
+
+    public Map<String, PomFilter> getPomFilters() {
+        return pomFilters;
+    }
+
+    protected BasePomFilterContainer newInstance() {
+        return new BasePomFilterContainer(mavenPomFactory);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/ClassifierArtifact.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/ClassifierArtifact.java
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/ClassifierArtifact.java
rename to subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/ClassifierArtifact.java
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/CustomDeployTask.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/CustomDeployTask.java
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/CustomDeployTask.java
rename to subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/CustomDeployTask.java
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/CustomInstallDeployTaskSupport.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/CustomInstallDeployTaskSupport.java
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/CustomInstallDeployTaskSupport.java
rename to subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/CustomInstallDeployTaskSupport.java
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/CustomInstallTask.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/CustomInstallTask.java
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/CustomInstallTask.java
rename to subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/CustomInstallTask.java
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultArtifactPom.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultArtifactPom.java
new file mode 100644
index 0000000..a7cfe2e
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultArtifactPom.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+import com.google.common.collect.Sets;
+import org.apache.commons.lang.ObjectUtils;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.maven.project.MavenProject;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.artifacts.Dependency;
+import org.gradle.api.artifacts.PublishArtifact;
+import org.gradle.api.artifacts.maven.MavenPom;
+import org.gradle.api.internal.artifacts.publish.AbstractPublishArtifact;
+import org.gradle.api.internal.artifacts.publish.maven.ArtifactPom;
+
+import java.io.File;
+import java.util.*;
+
+/**
+ * @author Hans Dockter
+ */
+public class DefaultArtifactPom implements ArtifactPom {
+    private static final Set<String> PACKAGING_TYPES = Sets.newHashSet("war", "jar", "ear");
+    private final MavenPom pom;
+    private final Map<ArtifactKey, PublishArtifact> artifacts = new HashMap<ArtifactKey, PublishArtifact>();
+
+    private PublishArtifact artifact;
+
+    private final Set<PublishArtifact> classifiers = new HashSet<PublishArtifact>();
+
+    public DefaultArtifactPom(MavenPom pom) {
+        this.pom = pom;
+    }
+
+    public MavenPom getPom() {
+        return pom;
+    }
+
+    public PublishArtifact getArtifact() {
+        return artifact;
+    }
+
+    public Set<PublishArtifact> getAttachedArtifacts() {
+        return Collections.unmodifiableSet(classifiers);
+    }
+
+    public PublishArtifact writePom(final File pomFile) {
+        getPom().writeTo(pomFile);
+        return new PomArtifact(pomFile);
+    }
+
+    public void addArtifact(Artifact artifact, File src) {
+        throwExceptionIfArtifactOrSrcIsNull(artifact, src);
+        PublishArtifact publishArtifact = new MavenArtifact(artifact, src);
+        ArtifactKey artifactKey = new ArtifactKey(publishArtifact);
+        if (this.artifacts.containsKey(artifactKey)) {
+            throw new InvalidUserDataException(String.format("A POM cannot have multiple artifacts with the same type and classifier. Already have %s, trying to add %s.", this.artifacts.get(
+                    artifactKey), publishArtifact));
+        }
+
+        if (publishArtifact.getClassifier() != null) {
+            addArtifact(publishArtifact);
+            assignArtifactValuesToPom(artifact, pom, false);
+            return;
+        }
+
+        if (this.artifact != null) {
+            // Choose the 'main' artifact based on its type.
+            if (!PACKAGING_TYPES.contains(artifact.getType())) {
+                addArtifact(publishArtifact);
+                return;
+            }
+            if (PACKAGING_TYPES.contains(this.artifact.getType())) {
+                throw new InvalidUserDataException("A POM can not have multiple main artifacts. " + "Already have " + this.artifact + ", trying to add " + publishArtifact);
+            }
+            addArtifact(this.artifact);
+        }
+
+        this.artifact = publishArtifact;
+        this.artifacts.put(artifactKey, publishArtifact);
+        assignArtifactValuesToPom(artifact, pom, true);
+    }
+
+    private void addArtifact(PublishArtifact artifact) {
+        classifiers.add(artifact);
+        artifacts.put(new ArtifactKey(artifact), artifact);
+    }
+
+    private String getClassifier(Artifact artifact) {
+        return artifact.getExtraAttribute(Dependency.CLASSIFIER);
+    }
+
+    private void assignArtifactValuesToPom(Artifact artifact, MavenPom pom, boolean setType) {
+        if (pom.getGroupId().equals(MavenProject.EMPTY_PROJECT_GROUP_ID)) {
+            pom.setGroupId(artifact.getModuleRevisionId().getOrganisation());
+        }
+        if (pom.getArtifactId().equals(MavenProject.EMPTY_PROJECT_ARTIFACT_ID)) {
+            pom.setArtifactId(artifact.getName());
+        }
+        if (pom.getVersion().equals(MavenProject.EMPTY_PROJECT_VERSION)) {
+            pom.setVersion(artifact.getModuleRevisionId().getRevision());
+        }
+        if (setType) {
+            pom.setPackaging(artifact.getType());
+        }
+    }
+
+    private void throwExceptionIfArtifactOrSrcIsNull(Artifact artifact, File src) {
+        if (artifact == null) {
+            throw new InvalidUserDataException("Artifact must not be null.");
+        }
+        if (src == null) {
+            throw new InvalidUserDataException("Src file must not be null.");
+        }
+    }
+
+    private static class ArtifactKey {
+        private final String type;
+        private final String classifier;
+
+        private ArtifactKey(PublishArtifact artifact) {
+            this.type = artifact.getType();
+            this.classifier = artifact.getClassifier();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            ArtifactKey other = (ArtifactKey) o;
+            return ObjectUtils.equals(type, other.type) && ObjectUtils.equals(classifier, other.classifier);
+        }
+
+        @Override
+        public int hashCode() {
+            return ObjectUtils.hashCode(type) ^ ObjectUtils.hashCode(classifier);
+        }
+    }
+
+    private abstract class AbstractMavenArtifact extends AbstractPublishArtifact {
+        private final File file;
+
+        protected AbstractMavenArtifact(File file) {
+            this.file = file;
+        }
+
+        public File getFile() {
+            return file;
+        }
+
+        public String getName() {
+            return pom.getArtifactId();
+        }
+
+        public Date getDate() {
+            return null;
+        }
+    }
+
+    private class MavenArtifact extends AbstractMavenArtifact {
+        private final Artifact artifact;
+
+        private MavenArtifact(Artifact artifact, File file) {
+            super(file);
+            this.artifact = artifact;
+        }
+
+        public String getClassifier() {
+            return DefaultArtifactPom.this.getClassifier(artifact);
+        }
+
+        public String getExtension() {
+            return artifact.getExt();
+        }
+
+        public String getType() {
+            return artifact.getType();
+        }
+    }
+
+    private class PomArtifact extends AbstractMavenArtifact {
+        public PomArtifact(File pomFile) {
+            super(pomFile);
+        }
+
+        public String getExtension() {
+            return "pom";
+        }
+
+        public String getType() {
+            return "pom";
+        }
+
+        public String getClassifier() {
+            return null;
+        }
+
+        public Date getDate() {
+            return null;
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultArtifactPomContainer.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultArtifactPomContainer.java
new file mode 100644
index 0000000..3401b53
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultArtifactPomContainer.java
@@ -0,0 +1,78 @@
+/*
+ * 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;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.artifacts.PublishArtifact;
+import org.gradle.api.artifacts.maven.MavenDeployment;
+import org.gradle.api.artifacts.maven.PomFilterContainer;
+import org.gradle.api.internal.artifacts.publish.maven.ArtifactPom;
+import org.gradle.api.internal.artifacts.publish.maven.ArtifactPomContainer;
+import org.gradle.api.internal.artifacts.publish.maven.ArtifactPomFactory;
+import org.gradle.api.internal.artifacts.publish.maven.MavenPomMetaInfoProvider;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Hans Dockter
+ */
+public class DefaultArtifactPomContainer implements ArtifactPomContainer {
+    private Map<String, ArtifactPom> artifactPoms = new HashMap<String, ArtifactPom>();
+    private final MavenPomMetaInfoProvider pomMetaInfoProvider;
+    private PomFilterContainer pomFilterContainer;
+    private ArtifactPomFactory artifactPomFactory;
+
+    public DefaultArtifactPomContainer(MavenPomMetaInfoProvider pomMetaInfoProvider, PomFilterContainer pomFilterContainer,
+                                       ArtifactPomFactory artifactPomFactory) {
+        this.pomMetaInfoProvider = pomMetaInfoProvider;
+        this.pomFilterContainer = pomFilterContainer;
+        this.artifactPomFactory = artifactPomFactory;
+    }
+
+    public void addArtifact(Artifact artifact, File src) {
+        if (artifact == null || src == null) {
+            throw new InvalidUserDataException("Artifact or source file must not be null!");
+        }
+        for (PomFilter activePomFilter : pomFilterContainer.getActivePomFilters()) {
+            if (activePomFilter.getFilter().accept(artifact, src)) {
+                if (artifactPoms.get(activePomFilter.getName()) == null) {
+                    artifactPoms.put(activePomFilter.getName(), artifactPomFactory.createArtifactPom(activePomFilter.getPomTemplate()));
+                }
+                artifactPoms.get(activePomFilter.getName()).addArtifact(artifact, src); 
+            }
+        }
+    }
+
+    public Set<MavenDeployment> createDeployableFilesInfos() {
+        Set<MavenDeployment> mavenDeployments = new HashSet<MavenDeployment>();
+        for (String activeArtifactPomName : artifactPoms.keySet()) {
+            ArtifactPom activeArtifactPom = artifactPoms.get(activeArtifactPomName);
+            File pomFile = createPomFile(activeArtifactPomName);
+            PublishArtifact pomArtifact = activeArtifactPom.writePom(pomFile);
+            mavenDeployments.add(new DefaultMavenDeployment(pomArtifact, activeArtifactPom.getArtifact(), activeArtifactPom.getAttachedArtifacts()));
+        }
+        return mavenDeployments;
+    }
+
+    private File createPomFile(String artifactPomName) {
+        return new File(pomMetaInfoProvider.getMavenPomDir(), "pom-" + artifactPomName + ".xml");
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultDeployTaskFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultDeployTaskFactory.java
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultDeployTaskFactory.java
rename to subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultDeployTaskFactory.java
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultInstallTaskFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultInstallTaskFactory.java
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultInstallTaskFactory.java
rename to subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultInstallTaskFactory.java
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultMavenDeployment.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultMavenDeployment.java
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultMavenDeployment.java
rename to subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultMavenDeployment.java
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultPomFilter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultPomFilter.java
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultPomFilter.java
rename to subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultPomFilter.java
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/LoggingHelper.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/LoggingHelper.java
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/LoggingHelper.java
rename to subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/LoggingHelper.java
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenDeployer.groovy b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenDeployer.groovy
new file mode 100644
index 0000000..f67c794
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenDeployer.groovy
@@ -0,0 +1,51 @@
+/*
+ * 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.codehaus.groovy.runtime.InvokerHelper
+import org.gradle.api.artifacts.maven.GroovyMavenDeployer
+import org.gradle.api.artifacts.maven.PomFilterContainer
+import org.gradle.api.internal.artifacts.publish.maven.ArtifactPomContainer
+import org.gradle.api.internal.artifacts.publish.maven.deploy.BaseMavenDeployer
+import org.gradle.logging.LoggingManagerInternal
+
+/**
+ * @author Hans Dockter
+ */
+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, PomFilterContainer pomFilterContainer, ArtifactPomContainer artifactPomContainer, LoggingManagerInternal loggingManager) {
+        super(name, pomFilterContainer, artifactPomContainer, loggingManager)
+    }
+    
+    def methodMissing(String name, args) {
+        if (name == REPOSITORY_BUILDER || name == SNAPSHOT_REPOSITORY_BUILDER) {
+            Object repository = InvokerHelper.invokeMethod(repositoryBuilder, REPOSITORY_BUILDER, args)
+            if (name == REPOSITORY_BUILDER) {
+                setRepository(repository)
+            } else {
+                setSnapshotRepository(repository)
+            }
+            return repository;
+        } else {
+            throw new MissingMethodException(name, this.class, args)
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/RepositoryBuilder.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/RepositoryBuilder.java
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/RepositoryBuilder.java
rename to subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/RepositoryBuilder.java
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/RepositoryFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/RepositoryFactory.java
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/RepositoryFactory.java
rename to subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/RepositoryFactory.java
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/mvnsettings/EmptyMavenSettingsSupplier.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/mvnsettings/EmptyMavenSettingsSupplier.java
new file mode 100644
index 0000000..d369abd
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/mvnsettings/EmptyMavenSettingsSupplier.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.mvnsettings;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.maven.artifact.ant.InstallDeployTaskSupport;
+import org.gradle.api.UncheckedIOException;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Author: Szczepan Faber, created at: 3/29/11
+ */
+public class EmptyMavenSettingsSupplier implements MavenSettingsSupplier {
+
+    private File settingsXml;
+
+    public void supply(InstallDeployTaskSupport installDeployTaskSupport) {
+        try {
+            settingsXml = File.createTempFile("gradle_empty_settings", ".xml");
+            FileUtils.writeStringToFile(settingsXml, "<settings/>");
+            settingsXml.deleteOnExit();
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+        installDeployTaskSupport.setSettingsFile(settingsXml);
+    }
+
+    public void done() {
+        if (settingsXml != null) {
+            settingsXml.delete();
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/mvnsettings/MavenSettingsProvider.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/mvnsettings/MavenSettingsProvider.java
new file mode 100644
index 0000000..7307bda
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/mvnsettings/MavenSettingsProvider.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.mvnsettings;
+
+import java.io.File;
+
+/**
+* Author: Szczepan Faber, created at: 3/30/11
+*/
+public class MavenSettingsProvider {
+    public File getUserSettingsFile() {
+        File m2Dir = getUserMavenDir();
+        return new File(m2Dir, "settings.xml");
+    }
+
+    public File getUserMavenDir() {
+        File userHome = new File(System.getProperty("user.home"));
+        return new File(userHome, ".m2");
+    }
+
+    public File getLocalMavenRepository() {
+        File m2Dir = getUserMavenDir();
+        return new File(m2Dir, "repository");
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/mvnsettings/MavenSettingsSupplier.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/mvnsettings/MavenSettingsSupplier.java
new file mode 100644
index 0000000..4c8f79f
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/mvnsettings/MavenSettingsSupplier.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.mvnsettings;
+
+import org.apache.maven.artifact.ant.InstallDeployTaskSupport;
+
+/**
+ * Author: Szczepan Faber, created at: 3/29/11
+ */
+public interface MavenSettingsSupplier {
+    void done();
+    void supply(InstallDeployTaskSupport installDeployTaskSupport);
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/mvnsettings/MaybeUserMavenSettingsSupplier.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/mvnsettings/MaybeUserMavenSettingsSupplier.java
new file mode 100644
index 0000000..058355c
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/mvnsettings/MaybeUserMavenSettingsSupplier.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.mvnsettings;
+
+import org.apache.maven.artifact.ant.InstallDeployTaskSupport;
+
+import java.io.File;
+
+/**
+ * Author: Szczepan Faber, created at: 3/29/11
+ */
+public class MaybeUserMavenSettingsSupplier implements MavenSettingsSupplier {
+
+    EmptyMavenSettingsSupplier emptySettingsSupplier = new EmptyMavenSettingsSupplier();
+    MavenSettingsProvider mavenSettingsProvider = new MavenSettingsProvider();
+
+    public void supply(InstallDeployTaskSupport installDeployTaskSupport) {
+        File userSettings = mavenSettingsProvider.getUserSettingsFile();
+        if (userSettings.exists()) {
+            installDeployTaskSupport.setSettingsFile(userSettings);
+            return;
+        }
+
+        emptySettingsSupplier.supply(installDeployTaskSupport);
+    }
+
+    public void done() {
+        emptySettingsSupplier.done();
+    }
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/pombuilder/CustomModelBuilder.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/pombuilder/CustomModelBuilder.java
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/pombuilder/CustomModelBuilder.java
rename to subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/pombuilder/CustomModelBuilder.java
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/pombuilder/ModelFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/pombuilder/ModelFactory.java
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/pombuilder/ModelFactory.java
rename to subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/pombuilder/ModelFactory.java
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/pombuilder/PlexusLoggerAdapter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/pombuilder/PlexusLoggerAdapter.java
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/pombuilder/PlexusLoggerAdapter.java
rename to subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/pombuilder/PlexusLoggerAdapter.java
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/CommonsHttpClientBackedRepository.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/CommonsHttpClientBackedRepository.java
new file mode 100644
index 0000000..e53f2df
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/CommonsHttpClientBackedRepository.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpMethod;
+import org.apache.commons.httpclient.HttpMethodRetryHandler;
+import org.apache.commons.httpclient.UsernamePasswordCredentials;
+import org.apache.commons.httpclient.auth.AuthScope;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.methods.PutMethod;
+import org.apache.commons.httpclient.methods.RequestEntity;
+import org.apache.commons.httpclient.params.HttpMethodParams;
+import org.apache.commons.io.output.CloseShieldOutputStream;
+import org.apache.ivy.plugins.repository.*;
+import org.apache.ivy.util.FileUtil;
+import org.gradle.util.GUtil;
+import org.gradle.util.GradleVersion;
+import org.gradle.util.UncheckedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A repository which uses commons-httpclient to access resources using HTTP/HTTPS.
+ */
+public class CommonsHttpClientBackedRepository extends AbstractRepository {
+    private static final Logger LOGGER = LoggerFactory.getLogger(CommonsHttpClientBackedRepository.class);
+    private final Map<String, HttpResource> resources = new HashMap<String, HttpResource>();
+    private final HttpClient client = new HttpClient();
+    private final RepositoryCopyProgressListener progress = new RepositoryCopyProgressListener(this);
+
+    public CommonsHttpClientBackedRepository(String username, String password) {
+        if (GUtil.isTrue(username)) {
+            client.getParams().setAuthenticationPreemptive(true);
+            client.getState().setCredentials(new AuthScope(null, -1, null), new UsernamePasswordCredentials(username, password));
+        }
+    }
+
+    public Resource getResource(final String source) throws IOException {
+        LOGGER.debug("Attempting to get resource {}.", source);
+        GetMethod method = new GetMethod(source);
+        configureMethod(method);
+        int result = client.executeMethod(method);
+        if (result == 404) {
+            return new MissingResource(source);
+        }
+        if (result != 200) {
+            throw new IOException(String.format("Could not GET '%s'. Received status code %s from server: %s", source, result, method.getStatusText()));
+        }
+
+        HttpResource resource = new HttpResource(source, method);
+        resources.put(source, resource);
+        return resource;
+    }
+
+    public void get(String source, File destination) throws IOException {
+        HttpResource resource = resources.get(source);
+        fireTransferInitiated(resource, TransferEvent.REQUEST_GET);
+        try {
+            progress.setTotalLength(resource.getContentLength());
+            resource.downloadTo(destination);
+        } catch (IOException e) {
+            fireTransferError(e);
+            throw e;
+        } catch (Exception e) {
+            fireTransferError(e);
+            throw UncheckedException.asUncheckedException(e);
+        } finally {
+            progress.setTotalLength(null);
+        }
+    }
+
+    @Override
+    protected void put(final File source, String destination, boolean overwrite) throws IOException {
+        LOGGER.debug("Attempting to put resource {}.", destination);
+        assert source.isFile();
+        fireTransferInitiated(new BasicResource(destination, true, source.length(), source.lastModified(), false), TransferEvent.REQUEST_PUT);
+        try {
+            progress.setTotalLength(source.length());
+            doPut(source, destination);
+        } catch (IOException e) {
+            fireTransferError(e);
+            throw e;
+        } catch (Exception e) {
+            fireTransferError(e);
+            throw UncheckedException.asUncheckedException(e);
+        } finally {
+            progress.setTotalLength(null);
+        }
+    }
+
+    private void doPut(File source, String destination) throws IOException {
+        PutMethod method = new PutMethod(destination);
+        configureMethod(method);
+        method.setRequestEntity(new FileRequestEntity(source));
+        int result = client.executeMethod(method);
+        if (result != 200) {
+            throw new IOException(String.format("Could not PUT '%s'. Received status code %s from server: %s", destination, result, method.getStatusText()));
+        }
+    }
+
+    private void configureMethod(HttpMethod method) {
+        method.setRequestHeader("User-Agent", "Gradle/" + GradleVersion.current().getVersion());
+        method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new HttpMethodRetryHandler() {
+            public boolean retryMethod(HttpMethod method, IOException exception, int executionCount) {
+                return false;
+            }
+        });
+    }
+
+    public List list(String parent) throws IOException {
+        return Collections.EMPTY_LIST;
+    }
+
+    private class HttpResource implements Resource {
+        private final String source;
+        private final GetMethod method;
+
+        public HttpResource(String source, GetMethod method) {
+            this.source = source;
+            this.method = method;
+        }
+
+        public String getName() {
+            return source;
+        }
+
+        @Override
+        public String toString() {
+            return getName();
+        }
+
+        public long getLastModified() {
+            return 0;
+        }
+
+        public long getContentLength() {
+            return method.getResponseContentLength();
+        }
+
+        public boolean exists() {
+            return true;
+        }
+
+        public boolean isLocal() {
+            return false;
+        }
+
+        public Resource clone(String cloneName) {
+            throw new UnsupportedOperationException();
+        }
+
+        public InputStream openStream() throws IOException {
+            return method.getResponseBodyAsStream();
+        }
+
+        public void downloadTo(File destination) throws IOException {
+            FileOutputStream output = new FileOutputStream(destination);
+            try {
+                InputStream input = openStream();
+                try {
+                    FileUtil.copy(input, output, progress);
+                } finally {
+                    input.close();
+                }
+            } finally {
+                output.close();
+            }
+        }
+    }
+
+    private static class MissingResource implements Resource {
+        private final String source;
+
+        public MissingResource(String source) {
+            this.source = source;
+        }
+
+        public Resource clone(String cloneName) {
+            throw new UnsupportedOperationException();
+        }
+
+        public String getName() {
+            return source;
+        }
+
+        public long getLastModified() {
+            throw new UnsupportedOperationException();
+        }
+
+        public long getContentLength() {
+            throw new UnsupportedOperationException();
+        }
+
+        public boolean exists() {
+            return false;
+        }
+
+        public boolean isLocal() {
+            throw new UnsupportedOperationException();
+        }
+
+        public InputStream openStream() throws IOException {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    private class FileRequestEntity implements RequestEntity {
+        private final File source;
+
+        public FileRequestEntity(File source) {
+            this.source = source;
+        }
+
+        public boolean isRepeatable() {
+            return false;
+        }
+
+        public void writeRequest(OutputStream out) throws IOException {
+            FileInputStream inputStream = new FileInputStream(source);
+            try {
+                FileUtil.copy(inputStream, new CloseShieldOutputStream(out), progress);
+            } finally {
+                inputStream.close();
+            }
+        }
+
+        public long getContentLength() {
+            return source.length();
+        }
+
+        public String getContentType() {
+            return "application/octet-stream";
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultIvyArtifactRepository.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultIvyArtifactRepository.java
new file mode 100644
index 0000000..98e04e4
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultIvyArtifactRepository.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+import org.apache.ivy.plugins.resolver.RepositoryResolver;
+import org.apache.ivy.plugins.resolver.URLResolver;
+import org.gradle.api.artifacts.dsl.IvyArtifactRepository;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.*;
+
+public class DefaultIvyArtifactRepository implements IvyArtifactRepository, ArtifactRepositoryInternal {
+    private String name;
+    private String username;
+    private String password;
+    private final Set<String> artifactPatterns = new LinkedHashSet<String>();
+
+    public void createResolvers(Collection<DependencyResolver> resolvers) {
+        List<String> httpPatterns = new ArrayList<String>();
+        List<String> otherPatterns = new ArrayList<String>();
+
+        for (String artifactPattern : artifactPatterns) {
+            try {
+                URI uri = new URI(artifactPattern.replaceAll("\\[.*\\]", "token"));
+                if (uri.getScheme() != null && (uri.getScheme().equalsIgnoreCase("http") || uri.getScheme().equalsIgnoreCase("https"))) {
+                    httpPatterns.add(artifactPattern);
+                    continue;
+                }
+            } catch (URISyntaxException e) {
+                // Ignore
+            }
+            otherPatterns.add(artifactPattern);
+        }
+
+        if (!otherPatterns.isEmpty()) {
+            URLResolver resolver = new URLResolver();
+            resolver.setName(name);
+            for (String artifactPattern : otherPatterns) {
+                resolver.addArtifactPattern(artifactPattern);
+                resolver.addIvyPattern(artifactPattern);
+            }
+            resolvers.add(resolver);
+        }
+
+        if (!httpPatterns.isEmpty()) {
+            RepositoryResolver resolver = new RepositoryResolver();
+            resolver.setRepository(new CommonsHttpClientBackedRepository(username, password));
+            resolver.setName(name);
+            for (String artifactPattern : httpPatterns) {
+                resolver.addArtifactPattern(artifactPattern);
+                resolver.addIvyPattern(artifactPattern);
+            }
+            resolvers.add(resolver);
+        }
+    }
+
+    public void artifactPattern(String pattern) {
+        artifactPatterns.add(pattern);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getUserName() {
+        return username;
+    }
+
+    public void setUserName(String username) {
+        this.username = username;
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultResolverFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultResolverFactory.java
new file mode 100644
index 0000000..ca92521
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultResolverFactory.java
@@ -0,0 +1,166 @@
+/*
+ * 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.internal.artifacts.repositories;
+
+import org.apache.ivy.plugins.resolver.*;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.artifacts.ConfigurationContainer;
+import org.gradle.api.artifacts.ResolverContainer;
+import org.gradle.api.artifacts.dsl.IvyArtifactRepository;
+import org.gradle.api.artifacts.maven.*;
+import org.gradle.api.internal.Factory;
+import org.gradle.api.internal.artifacts.ivyservice.GradleIBiblioResolver;
+import org.gradle.api.internal.artifacts.ivyservice.LocalFileRepositoryCacheManager;
+import org.gradle.api.internal.artifacts.ivyservice.ResolverFactory;
+import org.gradle.api.internal.artifacts.publish.maven.*;
+import org.gradle.api.internal.artifacts.publish.maven.deploy.*;
+import org.gradle.api.internal.artifacts.publish.maven.deploy.groovy.DefaultGroovyMavenDeployer;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.logging.LoggingManagerInternal;
+
+import java.io.File;
+import java.util.Map;
+
+/**
+ * @author Hans Dockter
+ */
+public class DefaultResolverFactory implements ResolverFactory {
+    private final Factory<LoggingManagerInternal> loggingManagerFactory;
+    private final MavenFactory mavenFactory;
+    private final LocalMavenCacheLocator localMavenCacheLocator;
+
+    public DefaultResolverFactory(Factory<LoggingManagerInternal> loggingManagerFactory, MavenFactory mavenFactory, LocalMavenCacheLocator localMavenCacheLocator) {
+        this.loggingManagerFactory = loggingManagerFactory;
+        this.mavenFactory = mavenFactory;
+        this.localMavenCacheLocator = localMavenCacheLocator;
+    }
+
+    public DependencyResolver createResolver(Object userDescription) {
+        DependencyResolver result;
+        if (userDescription instanceof String) {
+            result = createMavenRepoResolver((String) userDescription, (String) userDescription);
+        } else if (userDescription instanceof Map) {
+            Map<String, String> userDescriptionMap = (Map<String, String>) userDescription;
+            result = createMavenRepoResolver(userDescriptionMap.get(ResolverContainer.RESOLVER_NAME),
+                    userDescriptionMap.get(ResolverContainer.RESOLVER_URL));
+        } else if (userDescription instanceof DependencyResolver) {
+            result = (DependencyResolver) userDescription;
+        } else {
+            throw new InvalidUserDataException("Illegal Resolver type");
+        }
+        return result;
+    }
+
+    public FileSystemResolver createFlatDirResolver(String name, File... roots) {
+        FileSystemResolver resolver = new FileSystemResolver();
+        resolver.setName(name);
+        for (File root : roots) {
+            resolver.addArtifactPattern(root.getAbsolutePath() + "/[artifact]-[revision](-[classifier]).[ext]");
+            resolver.addArtifactPattern(root.getAbsolutePath() + "/[artifact](-[classifier]).[ext]");
+        }
+        resolver.setValidate(false);
+        resolver.setRepositoryCacheManager(new LocalFileRepositoryCacheManager(name));
+        return resolver;
+    }
+
+    public AbstractResolver createMavenLocalResolver(String name) {
+        String cacheDir = localMavenCacheLocator.getLocalMavenCache().toURI().toString();
+        return createMavenRepoResolver(name, cacheDir);
+    }
+
+    public AbstractResolver createMavenRepoResolver(String name, String root, String... jarRepoUrls) {
+        GradleIBiblioResolver iBiblioResolver = createIBiblioResolver(name, root);
+        if (jarRepoUrls.length == 0) {
+            iBiblioResolver.setDescriptor(IBiblioResolver.DESCRIPTOR_OPTIONAL);
+            return iBiblioResolver;
+        }
+        iBiblioResolver.setName(iBiblioResolver.getName() + "_poms");
+        URLResolver urlResolver = createUrlResolver(name, root, jarRepoUrls);
+        return createDualResolver(name, iBiblioResolver, urlResolver);
+    }
+
+    private GradleIBiblioResolver createIBiblioResolver(String name, String root) {
+        GradleIBiblioResolver iBiblioResolver = new GradleIBiblioResolver();
+        iBiblioResolver.setUsepoms(true);
+        iBiblioResolver.setName(name);
+        iBiblioResolver.setRoot(root);
+        iBiblioResolver.setPattern(ResolverContainer.MAVEN_REPO_PATTERN);
+        iBiblioResolver.setM2compatible(true);
+        iBiblioResolver.setUseMavenMetadata(true);
+        return iBiblioResolver;
+    }
+
+    private URLResolver createUrlResolver(String name, String root, String... jarRepoUrls) {
+        URLResolver urlResolver = new URLResolver();
+        urlResolver.setName(name + "_jars");
+        urlResolver.setM2compatible(true);
+        urlResolver.addArtifactPattern(root + '/' + ResolverContainer.MAVEN_REPO_PATTERN);
+        for (String jarRepoUrl : jarRepoUrls) {
+            urlResolver.addArtifactPattern(jarRepoUrl + '/' + ResolverContainer.MAVEN_REPO_PATTERN);
+        }
+        return urlResolver;
+    }
+
+    private DualResolver createDualResolver(String name, GradleIBiblioResolver iBiblioResolver, URLResolver urlResolver) {
+        DualResolver dualResolver = new DualResolver();
+        dualResolver.setName(name);
+        dualResolver.setIvyResolver(iBiblioResolver);
+        dualResolver.setArtifactResolver(urlResolver);
+        dualResolver.setDescriptor(DualResolver.DESCRIPTOR_OPTIONAL);
+        return dualResolver;
+    }
+
+    // todo use MavenPluginConvention pom factory after modularization is done
+
+    public GroovyMavenDeployer createMavenDeployer(String name, MavenPomMetaInfoProvider pomMetaInfoProvider,
+                                                   ConfigurationContainer configurationContainer,
+                                                   Conf2ScopeMappingContainer scopeMapping, FileResolver fileResolver) {
+        PomFilterContainer pomFilterContainer = createPomFilterContainer(
+                mavenFactory.createMavenPomFactory(configurationContainer, scopeMapping, fileResolver));
+        return new DefaultGroovyMavenDeployer(name, pomFilterContainer, createArtifactPomContainer(
+                pomMetaInfoProvider, pomFilterContainer, createArtifactPomFactory()), loggingManagerFactory.create());
+    }
+
+    // todo use MavenPluginConvention pom factory after modularization is done
+
+    public MavenResolver createMavenInstaller(String name, MavenPomMetaInfoProvider pomMetaInfoProvider,
+                                              ConfigurationContainer configurationContainer,
+                                              Conf2ScopeMappingContainer scopeMapping, FileResolver fileResolver) {
+        PomFilterContainer pomFilterContainer = createPomFilterContainer(
+                mavenFactory.createMavenPomFactory(configurationContainer, scopeMapping, fileResolver));
+        return new BaseMavenInstaller(name, pomFilterContainer, createArtifactPomContainer(pomMetaInfoProvider,
+                pomFilterContainer, createArtifactPomFactory()), loggingManagerFactory.create());
+    }
+
+    public IvyArtifactRepository createIvyRepository() {
+        return new DefaultIvyArtifactRepository();
+    }
+
+    private PomFilterContainer createPomFilterContainer(Factory<MavenPom> mavenPomFactory) {
+        return new BasePomFilterContainer(mavenPomFactory);
+    }
+
+    private ArtifactPomFactory createArtifactPomFactory() {
+        return new DefaultArtifactPomFactory();
+    }
+
+    private ArtifactPomContainer createArtifactPomContainer(MavenPomMetaInfoProvider pomMetaInfoProvider, PomFilterContainer filterContainer,
+                                                           ArtifactPomFactory pomFactory) {
+        return new DefaultArtifactPomContainer(pomMetaInfoProvider, filterContainer, pomFactory);
+    }
+
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DependencyManagementServicesTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DependencyManagementServicesTest.groovy
new file mode 100644
index 0000000..e884e73
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DependencyManagementServicesTest.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.api.artifacts.maven.MavenFactory
+import org.gradle.api.internal.artifacts.ivyservice.ResolverFactory
+import spock.lang.Specification
+import org.gradle.api.internal.project.ServiceRegistry
+import org.gradle.logging.LoggingManagerInternal
+
+class DependencyManagementServicesTest extends Specification {
+    final ServiceRegistry parent = Mock()
+    final DefaultDependencyManagementServices services = new DefaultDependencyManagementServices(parent)
+
+    def providesAResolverFactory() {
+        given:
+        _ * parent.getFactory(LoggingManagerInternal.class)
+
+        expect:
+        services.get(ResolverFactory.class) != null
+    }
+
+    def providesAMavenFactory() {
+        expect:
+        services.get(MavenFactory.class) != null
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultDeployTaskFactoryTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultDeployTaskFactoryTest.java
new file mode 100644
index 0000000..bc7133f
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultDeployTaskFactoryTest.java
@@ -0,0 +1,31 @@
+/*
+ * 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 static org.junit.Assert.assertTrue;
+import org.gradle.api.internal.artifacts.publish.maven.deploy.CustomDeployTask;
+import org.gradle.api.internal.artifacts.publish.maven.deploy.DefaultDeployTaskFactory;
+import org.junit.Test;
+
+/**
+ * @author Hans Dockter
+ */
+public class DefaultDeployTaskFactoryTest {
+    @Test
+    public void create() {
+        assertTrue(new DefaultDeployTaskFactory().create() instanceof CustomDeployTask);
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultLocalMavenCacheLocatorTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultLocalMavenCacheLocatorTest.groovy
new file mode 100644
index 0000000..f8db7b0
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultLocalMavenCacheLocatorTest.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.internal.artifacts.publish.maven
+
+import spock.lang.Specification
+import org.junit.Rule
+import org.gradle.util.SetSystemProperties
+import org.gradle.util.TemporaryFolder
+
+class DefaultLocalMavenCacheLocatorTest extends Specification {
+    @Rule public final SetSystemProperties systemProperties = new SetSystemProperties()
+    @Rule public final TemporaryFolder tmpDir = new TemporaryFolder()
+    final DefaultLocalMavenCacheLocator locator = new DefaultLocalMavenCacheLocator()
+
+    def setup() {
+        System.setProperty('user.home', tmpDir.dir.absolutePath)
+    }
+
+    def usesDefaultWhenNoSettingsXmlFile() {
+        expect:
+        locator.localMavenCache == new File(tmpDir.dir, '.m2/repository')
+    }
+
+    def usesValueFromSettingsXmlFile() {
+        tmpDir.file('.m2/settings.xml') << """
+<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <localRepository>${tmpDir.file('.m2/custom').absolutePath}</localRepository>
+</settings>"""
+
+        expect:
+        locator.localMavenCache == new File(tmpDir.dir, '.m2/custom')
+    }
+
+    def usesValueWithPlaceholderFromSettingsXmlFile() {
+        tmpDir.file('.m2/settings.xml') << '''
+<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <localRepository>${user.home}/.m2/custom</localRepository>
+</settings>'''
+
+        expect:
+        locator.localMavenCache == new File(tmpDir.dir, '.m2/custom')
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomFactoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomFactoryTest.groovy
new file mode 100644
index 0000000..f756036
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomFactoryTest.groovy
@@ -0,0 +1,46 @@
+/*
+ * 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.ConfigurationContainer
+import org.gradle.api.internal.artifacts.publish.maven.dependencies.DefaultConf2ScopeMappingContainer
+
+import org.gradle.api.internal.file.FileResolver
+import spock.lang.Specification
+
+/**
+ * @author Hans Dockter
+ */
+public class DefaultMavenPomFactoryTest extends Specification {
+    def createMavenPom() {
+        DefaultConf2ScopeMappingContainer scopeMappings = new DefaultConf2ScopeMappingContainer();
+        PomDependenciesConverter pomDependenciesConverter = Mock(PomDependenciesConverter); 
+        ConfigurationContainer configurationContainer = Mock(ConfigurationContainer); 
+        FileResolver fileResolver = Mock(FileResolver); 
+        DefaultMavenPomFactory mavenPomFactory = new DefaultMavenPomFactory(configurationContainer, scopeMappings,
+                pomDependenciesConverter, fileResolver);
+        DefaultMavenPom mavenPom = (DefaultMavenPom) mavenPomFactory.create();
+
+        expect:
+        !scopeMappings.is(mavenPom.scopeMappings)
+        scopeMappings == mavenPom.scopeMappings
+        mavenPom.mavenProject != null
+        mavenPom.pomDependenciesConverter.is(pomDependenciesConverter)
+        mavenPom.configurations.is(configurationContainer)
+        mavenPom.fileResolver == fileResolver
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomTest.groovy
new file mode 100644
index 0000000..9bb7948
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomTest.groovy
@@ -0,0 +1,191 @@
+/*
+ * 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.apache.commons.lang.builder.EqualsBuilder
+import org.apache.maven.model.Dependency
+import org.apache.maven.model.Model
+import org.gradle.api.Action
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.artifacts.ConfigurationContainer
+import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer
+import org.gradle.api.internal.artifacts.publish.maven.dependencies.DefaultConf2ScopeMappingContainer
+
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.util.TemporaryFolder
+import org.gradle.util.TestFile
+import org.gradle.util.TextUtil
+import org.junit.Rule
+import spock.lang.Specification
+
+class DefaultMavenPomTest extends Specification {
+    static final String EXPECTED_PACKAGING = "something";
+    static final String EXPECTED_GROUP_ID = "someGroup";
+    static final String EXPECTED_ARTIFACT_ID = "artifactId";
+    static final String EXPECTED_VERSION = "version";
+
+    @Rule
+    TemporaryFolder tmpDir = new TemporaryFolder()
+
+    Conf2ScopeMappingContainer conf2ScopeMappingContainer = new DefaultConf2ScopeMappingContainer()
+    PomDependenciesConverter pomDependenciesConverterStub = Mock()
+    ConfigurationContainer configurationContainerStub = Mock()
+    FileResolver fileResolver = Mock()
+    DefaultMavenPom mavenPom = new DefaultMavenPom(configurationContainerStub, conf2ScopeMappingContainer, pomDependenciesConverterStub,
+            fileResolver)
+
+    void setup() {
+        mavenPom.packaging = EXPECTED_PACKAGING
+        mavenPom.groupId = EXPECTED_GROUP_ID
+        mavenPom.artifactId = EXPECTED_ARTIFACT_ID
+        mavenPom.version = EXPECTED_VERSION
+    }
+
+    def init() {
+        expect:
+        mavenPom.scopeMappings.is(conf2ScopeMappingContainer)
+        mavenPom.configurations.is(configurationContainerStub)
+        mavenPom.fileResolver.is(fileResolver)
+        mavenPom.mavenProject.modelVersion == "4.0.0"
+    }
+
+    def setModel() {
+        def newModel = new Model()
+
+        when:
+        mavenPom.model = newModel
+
+        then:
+        mavenPom.model.is(newModel)
+    }
+
+    def effectivePomShouldHaveGeneratedDependencies() {
+        Set configurations = [Mock(Configuration)]
+        configurationContainerStub.getAll() >> configurations
+        List generatedDependencies = [new Dependency(groupId: 'someGroup')]
+        List manuallyAddedDependencies = [new Dependency()]
+        pomDependenciesConverterStub.convert(conf2ScopeMappingContainer, configurations) >> generatedDependencies
+
+        when:
+        mavenPom.dependencies = manuallyAddedDependencies.clone()
+
+        then:
+        EqualsBuilder.reflectionEquals(mavenPom.getEffectivePom().getMavenProject().getDependencies(), manuallyAddedDependencies + generatedDependencies)
+
+        when:
+        mavenPom.dependencies = []
+
+        then:
+        mavenPom.getEffectivePom().getMavenProject().getDependencies() == generatedDependencies
+    }
+
+    def configureActionsShouldBeAppliedAgainstEffectivePom() {
+        mavenPom.configurations = null
+        when:
+        mavenPom.whenConfigured(new Action() {
+            void execute(def mavenPom) {
+                mavenPom.mavenProject.inceptionYear = '1999'
+            }
+        })
+
+        then:
+        mavenPom.effectivePom.mavenProject.inceptionYear == '1999'
+        mavenPom.mavenProject.inceptionYear == null
+    }
+
+
+    def writeShouldUseEffectivePom() {
+        Set configurations = [Mock(Configuration)]
+        configurationContainerStub.getAll() >> configurations
+        List generatedDependencies = [new Dependency(groupId: 'someGroup')]
+        pomDependenciesConverterStub.convert(conf2ScopeMappingContainer, configurations) >> generatedDependencies
+
+        when:
+        StringWriter pomWriter = new StringWriter()
+        mavenPom.writeTo pomWriter
+
+        then:
+        pomWriter.toString().contains('someGroup')
+    }
+
+    def effectivePomWithNullConfigurationsShouldWork() {
+        when:
+        mavenPom.configurations = null
+
+        then:
+        mavenPom.getEffectivePom().getMavenProject().getDependencies() == []
+    }
+
+    void projectBuilder() {
+        mavenPom.mavenProject.inceptionYear = '2007'
+        mavenPom.mavenProject.description = 'some description'
+        mavenPom.project {
+            inceptionYear '2008'
+            licenses {
+                license {
+                    name 'The Apache Software License, Version 2.0'
+                    url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+                    distribution 'repo'
+                }
+            }
+        }
+
+        expect:
+        mavenPom.mavenProject.modelVersion == "4.0.0"
+        mavenPom.version == EXPECTED_VERSION
+        mavenPom.mavenProject.description == 'some description'
+        mavenPom.mavenProject.inceptionYear == '2008'
+        mavenPom.mavenProject.licenses.size() == 1
+        mavenPom.mavenProject.licenses[0].name == 'The Apache Software License, Version 2.0'
+        mavenPom.mavenProject.licenses[0].url == 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+        mavenPom.mavenProject.licenses[0].distribution == 'repo'
+    }
+
+    void writeToShouldApplyXmlActions() {
+        mavenPom.configurations = null
+        StringWriter pomWriter = new StringWriter()
+
+        when:
+        mavenPom.withXml {xmlProvider ->
+            xmlProvider.asString().append('someAppendix')
+        }
+        mavenPom.writeTo(pomWriter);
+
+        then:
+        pomWriter.toString().endsWith("someAppendix")
+    }
+
+    void writeToWritesCorrectPom() {
+        mavenPom.configurations = null
+        TestFile pomFile = tmpDir.file('someNonexistingDir').file('someFile')
+        fileResolver.resolve('file') >> pomFile
+
+        when:
+        mavenPom.writeTo('file');
+
+        then:
+        pomFile.text == TextUtil.toPlatformLineSeparators('''<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>someGroup</groupId>
+  <artifactId>artifactId</artifactId>
+  <version>version</version>
+  <packaging>something</packaging>
+</project>
+''')
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultConf2ScopeMappingContainerTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultConf2ScopeMappingContainerTest.java
similarity index 100%
rename from subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultConf2ScopeMappingContainerTest.java
rename to subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultConf2ScopeMappingContainerTest.java
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultExcludeRuleConverterTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultExcludeRuleConverterTest.java
similarity index 100%
rename from subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultExcludeRuleConverterTest.java
rename to subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultExcludeRuleConverterTest.java
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultPomDependenciesConverterTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultPomDependenciesConverterTest.java
new file mode 100644
index 0000000..4dc012f
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultPomDependenciesConverterTest.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dependencies;
+
+import static java.util.Arrays.asList;
+import static org.gradle.util.WrapUtil.toMap;
+import static org.gradle.util.WrapUtil.toSet;
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.maven.model.Exclusion;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.Dependency;
+import org.gradle.api.artifacts.ExcludeRule;
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.artifacts.maven.Conf2ScopeMapping;
+import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
+import org.gradle.api.internal.artifacts.DefaultExcludeRule;
+import org.gradle.api.internal.artifacts.dependencies.DefaultDependencyArtifact;
+import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency;
+import org.gradle.api.internal.artifacts.publish.maven.ExcludeRuleConverter;
+import org.gradle.util.WrapUtil;
+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;
+
+/**
+ * @author Hans Dockter
+ */
+ at RunWith(JMock.class)
+public class DefaultPomDependenciesConverterTest {
+    private JUnit4Mockery context = new JUnit4Mockery();
+    
+    private DefaultPomDependenciesConverter dependenciesConverter;
+    private Conf2ScopeMappingContainer conf2ScopeMappingContainerMock = context.mock(Conf2ScopeMappingContainer.class);
+    private ExcludeRuleConverter excludeRuleConverterMock = context.mock(ExcludeRuleConverter.class);
+
+    private ModuleDependency dependency1;
+    private ModuleDependency dependency2;
+    private ModuleDependency dependency31;
+    private ModuleDependency dependency32;
+    private Configuration compileConfStub;
+    private Configuration testCompileConfStub;
+
+    @Before
+    public void setUp() {
+        setUpCommonDependenciesAndConfigurations();
+        dependenciesConverter = new DefaultPomDependenciesConverter(excludeRuleConverterMock);
+    }
+
+    private void setUpCommonDependenciesAndConfigurations() {
+        dependency1 = createDependency("org1", "name1", "rev1");
+        dependency2 = createDependency("org2", "name2", "rev2");
+        dependency2.addArtifact(new DefaultDependencyArtifact("name2", null, null, null, null));
+        dependency31 = createDependency("org3", "name3", "rev3");
+        dependency32 = createDependency("org3", "name3", "rev3");
+        dependency32.addArtifact(new DefaultDependencyArtifact("artifactName32", "type32", "ext", "classifier32", null));
+        compileConfStub = createNamedConfigurationStubWithDependencies("compile", dependency1, dependency31);
+        testCompileConfStub = createNamedConfigurationStubWithDependencies("testCompile", dependency2, dependency32);
+        context.checking(new Expectations() {{
+            allowing(conf2ScopeMappingContainerMock).getMapping(toSet(testCompileConfStub, compileConfStub)); will(returnValue(createMapping(testCompileConfStub, "test")));
+            allowing(conf2ScopeMappingContainerMock).getMapping(toSet(compileConfStub, testCompileConfStub)); will(returnValue(createMapping(testCompileConfStub, "test")));
+            allowing(conf2ScopeMappingContainerMock).getMapping(toSet(testCompileConfStub)); will(returnValue(createMapping(testCompileConfStub, "test")));
+            allowing(conf2ScopeMappingContainerMock).getMapping(toSet(compileConfStub)); will(returnValue(createMapping(compileConfStub, "compile")));
+        }});
+    }
+
+    private Conf2ScopeMapping createMapping(Configuration configuration, String scope) {
+        return new Conf2ScopeMapping(10, configuration, scope);
+    }
+
+    private Configuration createNamedConfigurationStubWithDependencies(final String confName, final ModuleDependency... dependencies) {
+        return createNamedConfigurationStubWithDependencies(confName, new HashSet<ExcludeRule>(), dependencies);
+    }
+    
+    private Configuration createNamedConfigurationStubWithDependencies(final String confName, final Set<ExcludeRule> excludeRules, final ModuleDependency... dependencies) {
+        final Configuration configurationStub = context.mock(Configuration.class, confName);
+        context.checking(new Expectations() {{
+            allowing(configurationStub).getName();
+            will(returnValue(confName));
+            allowing(configurationStub).getDependencies(ModuleDependency.class);
+            will(returnValue(toSet(dependencies)));
+            allowing(configurationStub).getExcludeRules();
+            will(returnValue(excludeRules));            
+        }});
+        return configurationStub;
+    }
+
+    private ModuleDependency createDependency(final String group, final String name, final String version) {
+        return new DefaultExternalModuleDependency(group, name, version);
+    }
+
+    @Test
+    public void init() {
+        assertSame(excludeRuleConverterMock, dependenciesConverter.getExcludeRuleConverter());
+    }
+
+    @Test
+    public void convert() {
+        Set<Configuration> configurations = toSet(compileConfStub, testCompileConfStub);
+        context.checking(new Expectations() {{
+            allowing(conf2ScopeMappingContainerMock).isSkipUnmappedConfs(); will(returnValue(false));
+        }});
+        List<org.apache.maven.model.Dependency> actualMavenDependencies = dependenciesConverter.convert(conf2ScopeMappingContainerMock, configurations);
+        assertEquals(3, actualMavenDependencies.size());
+        checkCommonMavenDependencies(actualMavenDependencies);
+    }
+
+    @Test
+    public void convertWithUnMappedConfAndSkipTrue() {
+        final Dependency dependency4 = createDependency("org4", "name4", "rev4");
+        final Configuration unmappedConfigurationStub = createNamedConfigurationStubWithDependencies("unmappedConf");
+        context.checking(new Expectations() {{
+            allowing(unmappedConfigurationStub).getDependencies();
+            will(returnValue(toSet(dependency4)));
+        }});
+        context.checking(new Expectations() {{
+            allowing(conf2ScopeMappingContainerMock).isSkipUnmappedConfs(); will(returnValue(true));
+            allowing(conf2ScopeMappingContainerMock).getMapping(asList(unmappedConfigurationStub)); will(returnValue(null));
+        }});
+        List<org.apache.maven.model.Dependency> actualMavenDependencies = dependenciesConverter.convert(conf2ScopeMappingContainerMock, toSet(
+                compileConfStub, testCompileConfStub, unmappedConfigurationStub));
+        assertEquals(3, actualMavenDependencies.size());
+        checkCommonMavenDependencies(actualMavenDependencies);
+    }
+
+    @Test
+    public void convertWithUnMappedConfAndSkipFalse() {
+        final ModuleDependency dependency4 = createDependency("org4", "name4", "rev4");
+        final Configuration unmappedConfigurationStub = createNamedConfigurationStubWithDependencies("unmappedConf", dependency4);
+        context.checking(new Expectations() {{
+            allowing(conf2ScopeMappingContainerMock).isSkipUnmappedConfs(); will(returnValue(false));
+            allowing(conf2ScopeMappingContainerMock).getMapping(toSet(unmappedConfigurationStub)); will(returnValue(new Conf2ScopeMapping(null, unmappedConfigurationStub, null)));
+        }});
+        List<org.apache.maven.model.Dependency> actualMavenDependencies = dependenciesConverter.convert(conf2ScopeMappingContainerMock, toSet(
+                compileConfStub, testCompileConfStub, unmappedConfigurationStub));
+        assertEquals(4, actualMavenDependencies.size());
+        checkCommonMavenDependencies(actualMavenDependencies);
+        assertTrue(hasDependency(actualMavenDependencies, "org4", "name4", "rev4", null, null, null, false));
+    }
+
+    private void checkCommonMavenDependencies(List<org.apache.maven.model.Dependency> actualMavenDependencies) {
+        assertTrue(hasDependency(actualMavenDependencies, "org1", "name1", "rev1", null, "compile", null, false));
+        assertTrue(hasDependency(actualMavenDependencies, "org2", "name2", "rev2", null, "test", null, false));
+        assertTrue(hasDependency(actualMavenDependencies, "org3", "artifactName32", "rev3", "type32", "test", "classifier32", false));
+    }
+
+    private boolean hasDependency(List<org.apache.maven.model.Dependency> mavenDependencies,
+                                  String group, String artifactId, String version, String type, String scope,
+                                  String classifier, boolean optional) {
+        org.apache.maven.model.Dependency expectedDependency = new org.apache.maven.model.Dependency();
+        expectedDependency.setGroupId(group);
+        expectedDependency.setArtifactId(artifactId);
+        expectedDependency.setVersion(version);
+        expectedDependency.setType(type);
+        expectedDependency.setScope(scope);
+        expectedDependency.setClassifier(classifier);
+        expectedDependency.setOptional(optional);
+        for (org.apache.maven.model.Dependency mavenDependency : mavenDependencies) {
+            if (equals(mavenDependency, expectedDependency)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean equals(org.apache.maven.model.Dependency lhs, org.apache.maven.model.Dependency rhs) {
+        if (!lhs.getGroupId().equals(lhs.getGroupId())) {
+            return false;
+        }
+        if (!lhs.getArtifactId().equals(lhs.getArtifactId())) {
+            return false;
+        }
+        if (!lhs.getVersion().equals(lhs.getVersion())) {
+            return false;
+        }
+        if (lhs.getType() != null ? !lhs.getType().equals(lhs.getType()) : rhs.getType() != null) {
+            return false;
+        }
+        if (lhs.getScope() != null ? !lhs.getScope().equals(lhs.getScope()) : rhs.getScope() != null) {
+            return false;
+        }
+        if (!lhs.isOptional() == lhs.isOptional()) {
+            return false;
+        }
+        if (lhs.getClassifier() != null ? !lhs.getClassifier().equals(rhs.getClassifier()) : rhs.getClassifier() != null) {
+            return false;
+        }
+        return true;
+    }
+
+    @Test
+    public void convertWithConvertableDependencyExcludes() {
+        final Configuration someConfigurationStub = createNamedConfigurationStubWithDependencies("someConfiguration", dependency1);
+        final Exclusion mavenExclude = new Exclusion();
+        mavenExclude.setGroupId("a");
+        mavenExclude.setArtifactId("b");
+        dependency1.exclude(toMap("key", "value"));
+        context.checking(new Expectations() {{
+           allowing(conf2ScopeMappingContainerMock).getMapping(toSet(someConfigurationStub)); will(returnValue(createMapping(compileConfStub, "compile")));
+           allowing(excludeRuleConverterMock).convert(dependency1.getExcludeRules().iterator().next()); will(returnValue(mavenExclude));
+        }});
+        List<org.apache.maven.model.Dependency> actualMavenDependencies = dependenciesConverter.convert(conf2ScopeMappingContainerMock, toSet(someConfigurationStub));
+        assertEquals(1, actualMavenDependencies.size());
+        assertTrue(hasDependency(actualMavenDependencies, "org1", "name1", "rev1", null, "compile", null, false));
+        org.apache.maven.model.Dependency mavenDependency = (org.apache.maven.model.Dependency) actualMavenDependencies.get(0);
+        assertThat(mavenDependency.getExclusions().size(), equalTo(1));
+        assertThat(((Exclusion) mavenDependency.getExclusions().get(0)).getGroupId(), equalTo(mavenExclude.getGroupId()));
+        assertThat(((Exclusion) mavenDependency.getExclusions().get(0)).getArtifactId(), equalTo(mavenExclude.getArtifactId()));
+    }
+    
+    @Test
+    public void convertWithConvertableConfigurationExcludes() {
+        final Configuration someConfigurationStub = createNamedConfigurationStubWithDependencies("someConfiguration", 
+                WrapUtil.<ExcludeRule>toSet(new DefaultExcludeRule(toMap("key", "value"))), dependency1);
+        final Exclusion mavenExclude = new Exclusion();
+        mavenExclude.setGroupId("a");
+        mavenExclude.setArtifactId("b");
+        context.checking(new Expectations() {{
+           allowing(conf2ScopeMappingContainerMock).getMapping(toSet(someConfigurationStub)); will(returnValue(createMapping(compileConfStub, "compile")));
+           allowing(excludeRuleConverterMock).convert(someConfigurationStub.getExcludeRules().iterator().next()); will(returnValue(mavenExclude));
+        }});
+        List<org.apache.maven.model.Dependency> actualMavenDependencies = dependenciesConverter.convert(conf2ScopeMappingContainerMock, toSet(someConfigurationStub));
+        assertEquals(1, actualMavenDependencies.size());
+        assertTrue(hasDependency(actualMavenDependencies, "org1", "name1", "rev1", null, "compile", null, false));
+        org.apache.maven.model.Dependency mavenDependency = (org.apache.maven.model.Dependency) actualMavenDependencies.get(0);
+        assertThat(mavenDependency.getExclusions().size(), equalTo(1));
+        assertThat(((Exclusion) mavenDependency.getExclusions().get(0)).getGroupId(), equalTo(mavenExclude.getGroupId()));
+        assertThat(((Exclusion) mavenDependency.getExclusions().get(0)).getArtifactId(), equalTo(mavenExclude.getArtifactId()));
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/AbstractMavenResolverTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/AbstractMavenResolverTest.java
new file mode 100644
index 0000000..477c8e4
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/AbstractMavenResolverTest.java
@@ -0,0 +1,239 @@
+/*
+ * 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;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.maven.artifact.ant.AttachedArtifact;
+import org.apache.maven.artifact.ant.InstallDeployTaskSupport;
+import org.apache.maven.artifact.ant.Pom;
+import org.apache.maven.settings.Settings;
+import org.apache.tools.ant.Project;
+import org.codehaus.plexus.PlexusContainerException;
+import org.gradle.api.Action;
+import org.gradle.api.artifacts.PublishArtifact;
+import org.gradle.api.artifacts.maven.MavenDeployment;
+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.DefaultPublishArtifact;
+import org.gradle.api.internal.artifacts.publish.maven.ArtifactPomContainer;
+import org.gradle.api.internal.artifacts.publish.maven.deploy.mvnsettings.MavenSettingsSupplier;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.logging.LoggingManagerInternal;
+import org.gradle.util.AntUtil;
+import org.gradle.util.JUnit4GroovyMockery;
+import org.gradle.util.WrapUtil;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.jmock.Expectations;
+import org.jmock.lib.legacy.ClassImposteriser;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Set;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Hans Dockter
+ */
+public abstract class AbstractMavenResolverTest {
+    public static final String TEST_NAME = "name";
+    private static final Artifact TEST_IVY_ARTIFACT = DefaultArtifact.newIvyArtifact(ModuleRevisionId.newInstance("org", TEST_NAME, "1.0"), null);
+    private static final File TEST_IVY_FILE = new File("somepom.xml");
+    private static final File TEST_JAR_FILE = new File("somejar.jar");
+    private static final Artifact TEST_ARTIFACT = new DefaultArtifact(ModuleRevisionId.newInstance("org", TEST_NAME, "1.0"), null, TEST_NAME, "jar", "jar");
+    protected ArtifactPomContainer artifactPomContainerMock;
+    protected PomFilterContainer pomFilterContainerMock;
+    protected LoggingManagerInternal loggingManagerMock;
+
+    protected JUnit4GroovyMockery context = new JUnit4GroovyMockery() {
+        {
+            setImposteriser(ClassImposteriser.INSTANCE);
+        }
+    };
+    protected MavenPom pomMock;
+
+    protected Settings mavenSettingsMock;
+
+    protected abstract AbstractMavenResolver getMavenResolver();
+
+    protected abstract InstallDeployTaskSupport getInstallDeployTask();
+
+    protected abstract PomFilterContainer createPomFilterContainerMock();
+
+    @Before
+    public void setUp() {
+        pomFilterContainerMock = createPomFilterContainerMock();
+        artifactPomContainerMock = context.mock(ArtifactPomContainer.class);
+        pomMock = context.mock(MavenPom.class);
+        mavenSettingsMock = context.mock(Settings.class);
+        loggingManagerMock = context.mock(LoggingManagerInternal.class);
+    }
+
+    @Test
+    public void deployOrInstall() throws IOException, PlexusContainerException {
+        getMavenResolver().mavenSettingsSupplier = context.mock(MavenSettingsSupplier.class);
+
+        PublishArtifact classifierArtifact = artifact(new File("classifier.jar"));
+        final DefaultMavenDeployment deployment1 = new DefaultMavenDeployment(artifact(new File("pom1.pom")), artifact(new File("artifact1.jar")), Collections.<PublishArtifact>emptySet());
+        final DefaultMavenDeployment deployment2 = new DefaultMavenDeployment(artifact(new File("pom2.pom")), artifact(new File("artifact2.jar")), WrapUtil.toSet(classifierArtifact));
+        final Set<DefaultMavenDeployment> testDefaultMavenDeployments = WrapUtil.toSet(
+                deployment1,
+                deployment2
+        );
+        final AttachedArtifact attachedArtifact = new AttachedArtifact();
+        final Action<MavenDeployment> action = context.mock(Action.class);
+
+        context.checking(new Expectations() {
+            {
+                allowing((CustomInstallDeployTaskSupport) getInstallDeployTask()).getSettings();
+                will(returnValue(mavenSettingsMock));
+                allowing((CustomInstallDeployTaskSupport) getInstallDeployTask()).getProject();
+                will(returnValue(AntUtil.createProject()));
+                allowing((CustomInstallDeployTaskSupport) getInstallDeployTask()).createAttach();
+                will(returnValue(attachedArtifact));
+                one(artifactPomContainerMock).addArtifact(TEST_ARTIFACT, TEST_JAR_FILE);
+                allowing(artifactPomContainerMock).createDeployableFilesInfos();
+                will(returnValue(testDefaultMavenDeployments));
+                one(action).execute(deployment1);
+                one(action).execute(deployment2);
+            }
+        });
+
+        getMavenResolver().beforeDeployment(action);
+        getMavenResolver().publish(TEST_IVY_ARTIFACT, TEST_IVY_FILE, true);
+        getMavenResolver().publish(TEST_ARTIFACT, TEST_JAR_FILE, true);
+        checkTransaction(testDefaultMavenDeployments, attachedArtifact, classifierArtifact);
+        assertSame(mavenSettingsMock, getMavenResolver().getSettings());
+    }
+
+    private PublishArtifact artifact(File file) {
+        return new DefaultPublishArtifact("name", "ext", "type", null, null, file);
+    }
+
+    protected void checkTransaction(final Set<DefaultMavenDeployment> defaultMavenDeployments, final AttachedArtifact attachedArtifact, PublishArtifact classifierArtifact) throws IOException, PlexusContainerException {
+        context.checking(new Expectations() {
+            {
+                one(getInstallDeployTask()).setProject(with(any(Project.class)));
+                for (DefaultMavenDeployment defaultMavenDeployment : defaultMavenDeployments) {
+                    one(getInstallDeployTask()).setFile(defaultMavenDeployment.getMainArtifact().getFile());
+                    one(getInstallDeployTask()).addPom(with(pomMatcher(defaultMavenDeployment.getPomArtifact().getFile(), getInstallDeployTask().getProject())));
+                    one(loggingManagerMock).captureStandardOutput(LogLevel.INFO);
+                    will(returnValue(loggingManagerMock));
+                    one(loggingManagerMock).start();
+                    one(getInstallDeployTask()).execute();
+                    one(loggingManagerMock).stop();
+                    will(returnValue(loggingManagerMock));
+                }
+                one(getMavenResolver().mavenSettingsSupplier).supply(getInstallDeployTask());
+                one(getMavenResolver().mavenSettingsSupplier).done();
+            }
+        });
+        getMavenResolver().commitPublishTransaction();
+        assertThat(attachedArtifact.getFile(), equalTo(classifierArtifact.getFile()));
+        assertThat(attachedArtifact.getType(), equalTo(classifierArtifact.getType()));
+        assertThat(attachedArtifact.getClassifier(), equalTo(classifierArtifact.getClassifier()));
+    }
+
+    private static Matcher<Pom> pomMatcher(final File expectedPomFile, final Project expectedAntProject) {
+        return new BaseMatcher<Pom>() {
+            public void describeTo(Description description) {
+                description.appendText("matching pom");
+            }
+
+            public boolean matches(Object actual) {
+                Pom actualPom = (Pom) actual;
+                return actualPom.getFile().equals(expectedPomFile) && actualPom.getProject().equals(expectedAntProject);
+            }
+        };
+    }
+
+    @Test
+    public void setFilter() {
+        final PublishFilter publishFilterMock = context.mock(PublishFilter.class);
+        context.checking(new Expectations() {{
+            one(pomFilterContainerMock).setFilter(publishFilterMock);
+        }});
+        getMavenResolver().setFilter(publishFilterMock);
+    }
+
+    @Test
+    public void getFilter() {
+        final PublishFilter publishFilterMock = context.mock(PublishFilter.class);
+        context.checking(new Expectations() {{
+            allowing(pomFilterContainerMock).getFilter();
+            will(returnValue(publishFilterMock));
+        }});
+        assertSame(publishFilterMock, getMavenResolver().getFilter());
+    }
+
+    @Test
+    public void setPom() {
+        context.checking(new Expectations() {{
+            one(pomFilterContainerMock).setPom(pomMock);
+        }});
+        getMavenResolver().setPom(pomMock);
+    }
+
+    @Test
+    public void getPom() {
+        context.checking(new Expectations() {{
+            allowing(pomFilterContainerMock).getPom();
+            will(returnValue(pomMock));
+        }});
+        assertSame(pomMock, getMavenResolver().getPom());
+    }
+
+    @Test
+    public void addFilter() {
+        final String testName = "somename";
+        final PublishFilter publishFilterMock = context.mock(PublishFilter.class);
+        context.checking(new Expectations() {{
+            one(pomFilterContainerMock).addFilter(testName, publishFilterMock);
+        }});
+        getMavenResolver().addFilter(testName, publishFilterMock);
+    }
+
+    @Test
+    public void filter() {
+        final String testName = "somename";
+        final PublishFilter publishFilterMock = context.mock(PublishFilter.class);
+        context.checking(new Expectations() {{
+            one(pomFilterContainerMock).filter(testName);
+            will(returnValue(publishFilterMock));
+        }});
+        assertSame(publishFilterMock, getMavenResolver().filter(testName));
+    }
+
+    @Test
+    public void pom() {
+        final String testName = "somename";
+        context.checking(new Expectations() {{
+            one(pomFilterContainerMock).pom(testName);
+            will(returnValue(pomMock));
+        }});
+        assertSame(pomMock, getMavenResolver().pom(testName));
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenDeployerTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenDeployerTest.java
new file mode 100644
index 0000000..bb0e53f
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenDeployerTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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;
+
+import org.apache.maven.artifact.ant.AttachedArtifact;
+import org.apache.maven.artifact.ant.InstallDeployTaskSupport;
+import org.apache.maven.artifact.ant.RemoteRepository;
+import org.codehaus.plexus.PlexusContainer;
+import org.codehaus.plexus.PlexusContainerException;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.PublishArtifact;
+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;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Set;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Hans Dockter
+ */
+ at RunWith(org.jmock.integration.junit4.JMock.class)
+public class BaseMavenDeployerTest extends AbstractMavenResolverTest {
+
+    private BaseMavenDeployer mavenDeployer = createMavenDeployer();
+
+    private Factory<CustomDeployTask> deployTaskFactoryMock = context.mock(Factory.class);
+    private CustomDeployTask deployTaskMock = context.mock(CustomDeployTask.class);
+
+    private PlexusContainer plexusContainerMock = context.mock(PlexusContainer.class);
+    private RemoteRepository testRepository = new RemoteRepository();
+    private RemoteRepository testSnapshotRepository = new RemoteRepository();
+
+    private Configuration configurationStub = context.mock(Configuration.class);
+
+    protected BaseMavenDeployer createMavenDeployer() {
+        return new BaseMavenDeployer(TEST_NAME, pomFilterContainerMock, artifactPomContainerMock, loggingManagerMock);
+    }
+
+    protected AbstractMavenResolver getMavenResolver() {
+        return mavenDeployer;
+    }
+
+    protected InstallDeployTaskSupport getInstallDeployTask() {
+        return deployTaskMock;
+    }
+
+    protected PomFilterContainer createPomFilterContainerMock() {
+        return context.mock(PomFilterContainer.class);
+    }
+
+    public void setUp() {
+        super.setUp();
+        mavenDeployer = createMavenDeployer();
+        mavenDeployer.setDeployTaskFactory(deployTaskFactoryMock);
+        mavenDeployer.setRepository(testRepository);
+        mavenDeployer.setSnapshotRepository(testSnapshotRepository);
+        mavenDeployer.setConfiguration(configurationStub);
+        mavenDeployer.setUniqueVersion(false);
+    }
+
+    protected void checkTransaction(final Set<DefaultMavenDeployment> defaultMavenDeployments, AttachedArtifact attachedArtifact, PublishArtifact classifierArtifact) throws IOException, PlexusContainerException {
+        final Set<File> protocolJars = WrapUtil.toLinkedSet(new File("jar1"), new File("jar1"));
+        context.checking(new Expectations() {{
+                allowing(configurationStub).resolve();
+                will(returnValue(protocolJars));
+                allowing(deployTaskFactoryMock).create();
+                will(returnValue(getInstallDeployTask()));
+                allowing(deployTaskMock).getContainer();
+                will(returnValue(plexusContainerMock));
+                for (File protocolProviderJar : protocolJars) {
+                    one(plexusContainerMock).addJarResource(protocolProviderJar);
+                }
+                one(deployTaskMock).setUniqueVersion(mavenDeployer.isUniqueVersion());
+                one(deployTaskMock).addRemoteRepository(testRepository);
+                one(deployTaskMock).addRemoteSnapshotRepository(testSnapshotRepository);
+        }});
+        super.checkTransaction(defaultMavenDeployments, attachedArtifact, classifierArtifact);
+    }
+
+    @Test
+    public void init() {
+        mavenDeployer = new BaseMavenDeployer(TEST_NAME, pomFilterContainerMock, artifactPomContainerMock, loggingManagerMock);
+        assertTrue(mavenDeployer.isUniqueVersion());
+    }
+
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenInstallerTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenInstallerTest.java
new file mode 100644
index 0000000..02bf3d1
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenInstallerTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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;
+
+import org.apache.maven.artifact.ant.AttachedArtifact;
+import org.apache.maven.artifact.ant.InstallDeployTaskSupport;
+import org.codehaus.plexus.PlexusContainerException;
+import org.gradle.api.artifacts.PublishArtifact;
+import org.gradle.api.artifacts.maven.PomFilterContainer;
+import org.gradle.api.internal.Factory;
+import org.jmock.Expectations;
+
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * @author Hans Dockter
+ */
+public class BaseMavenInstallerTest extends AbstractMavenResolverTest {
+    private BaseMavenInstaller mavenInstaller;
+
+    private Factory<CustomInstallTask> installTaskFactoryMock;
+    private CustomInstallTask installTaskMock;
+
+    protected BaseMavenInstaller createMavenInstaller() {
+        return new BaseMavenInstaller(TEST_NAME, pomFilterContainerMock, artifactPomContainerMock, loggingManagerMock);
+    }
+
+    protected PomFilterContainer createPomFilterContainerMock() {
+        return context.mock(PomFilterContainer.class);
+    }
+
+    protected AbstractMavenResolver getMavenResolver() {
+        return mavenInstaller;
+    }
+
+    protected InstallDeployTaskSupport getInstallDeployTask() {
+        return installTaskMock;
+    }
+
+    public void setUp() {
+        super.setUp();
+        installTaskFactoryMock = context.mock(Factory.class);
+        installTaskMock = context.mock(CustomInstallTask.class);
+        mavenInstaller = createMavenInstaller();
+        mavenInstaller.setInstallTaskFactory(installTaskFactoryMock);
+    }
+
+    protected void checkTransaction(final Set<DefaultMavenDeployment> deployableUnits, AttachedArtifact attachedArtifact, PublishArtifact classifierArtifact) throws IOException, PlexusContainerException {
+        context.checking(new Expectations() {
+            {
+                allowing(installTaskFactoryMock).create();
+                will(returnValue(getInstallDeployTask()));
+            }
+        });
+        super.checkTransaction(deployableUnits, attachedArtifact, classifierArtifact);
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BasePomFilterContainerTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BasePomFilterContainerTest.java
similarity index 100%
rename from subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BasePomFilterContainerTest.java
rename to subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BasePomFilterContainerTest.java
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultArtifactPomContainerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultArtifactPomContainerTest.groovy
new file mode 100644
index 0000000..4dc30af
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultArtifactPomContainerTest.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.api.internal.artifacts.publish.maven.deploy
+
+import spock.lang.Specification
+import org.gradle.api.internal.artifacts.publish.maven.MavenPomMetaInfoProvider
+import org.gradle.api.artifacts.maven.PomFilterContainer
+import org.apache.ivy.core.module.descriptor.Artifact
+import org.gradle.api.artifacts.maven.PublishFilter
+import org.gradle.api.artifacts.maven.MavenPom
+import org.gradle.api.artifacts.PublishArtifact
+import org.gradle.api.internal.artifacts.publish.maven.ArtifactPomFactory
+import org.gradle.api.internal.artifacts.publish.maven.ArtifactPom
+
+class DefaultArtifactPomContainerTest extends Specification {
+    final MavenPomMetaInfoProvider pomMetaInfoProvider = Mock()
+    final PomFilterContainer pomFilterContainer = Mock()
+    final ArtifactPomFactory artifactPomFactory = Mock()
+    final File pomDir = new File('pomDir')
+    final DefaultArtifactPomContainer container = new DefaultArtifactPomContainer(pomMetaInfoProvider, pomFilterContainer, artifactPomFactory)
+
+    def setup() {
+        _ * pomMetaInfoProvider.mavenPomDir >> pomDir
+    }
+    
+    def addsArtifactToFirstMatchingArtifactPom() {
+        File artifactFile = new File('artifact')
+        Artifact artifact = artifact()
+        MavenPom templatePom = Mock()
+        PomFilter filter1 = alwaysFilter('filterName', templatePom)
+        PomFilter filter2 = neverFilter()
+        ArtifactPom pom = pom('artifactId')
+        PublishArtifact pomArtifact = Mock()
+        PublishArtifact mainArtifact = Mock()
+        PublishArtifact attachArtifact = Mock()
+
+        when:
+        container.addArtifact(artifact, artifactFile)
+        def infos = container.createDeployableFilesInfos()
+
+        then:
+        _ * pomFilterContainer.activePomFilters >> [filter1, filter2]
+        _ * artifactPomFactory.createArtifactPom(templatePom) >> pom
+        _ * pom.writePom(new File(pomDir, 'pom-filterName.xml')) >> pomArtifact
+        _ * pom.artifact >> mainArtifact
+        _ * pom.attachedArtifacts >> ([attachArtifact] as Set)
+
+        infos.size() == 1
+        DefaultMavenDeployment info = infos.asList()[0]
+
+        info.pomArtifact == pomArtifact
+        info.mainArtifact == mainArtifact
+        info.artifacts == [pomArtifact, mainArtifact, attachArtifact] as Set
+        info.attachedArtifacts == [attachArtifact] as Set
+    }
+
+    def alwaysFilter(String filterName, MavenPom template) {
+        filter(filterName, true, template)
+    }
+
+    def neverFilter() {
+        filter('never', false)
+    }
+
+    def filter(String name, boolean accept, MavenPom template = null) {
+        PomFilter filter = Mock()
+        PublishFilter publishFilter = Mock()
+        _ * filter.name >> name
+        _ * filter.filter >> publishFilter
+        _ * publishFilter.accept(_, _) >> accept
+        _ * filter.pomTemplate >> template
+        filter
+    }
+
+    def artifact() {
+        Artifact artifact = Mock()
+        return artifact
+    }
+
+    def pom(String artifactId) {
+        ArtifactPom pom = Mock()
+        MavenPom mavenPom = Mock()
+        _ * pom.pom >> mavenPom
+        _ * mavenPom.artifactId >> artifactId
+        return pom
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultArtifactPomTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultArtifactPomTest.java
new file mode 100644
index 0000000..576c8be
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultArtifactPomTest.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.artifacts.ConfigurationContainer;
+import org.gradle.api.artifacts.Dependency;
+import org.gradle.api.artifacts.PublishArtifact;
+import org.gradle.api.artifacts.maven.MavenPom;
+import org.gradle.api.internal.artifacts.publish.maven.DefaultMavenPom;
+import org.gradle.api.internal.artifacts.publish.maven.dependencies.DefaultConf2ScopeMappingContainer;
+import org.gradle.api.internal.artifacts.publish.maven.PomDependenciesConverter;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.util.GUtil;
+import org.gradle.util.TemporaryFolder;
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+/**
+ * @author Hans Dockter
+ */
+public class DefaultArtifactPomTest {
+    private DefaultArtifactPom artifactPom;
+    private MavenPom testPom;
+
+    @Rule
+    public TemporaryFolder tmpDir = new TemporaryFolder();
+
+    Mockery context = new JUnit4Mockery();
+
+    @Before
+    public void setUp() {
+        testPom = new DefaultMavenPom(context.mock(ConfigurationContainer.class), new DefaultConf2ScopeMappingContainer(),
+                context.mock(PomDependenciesConverter.class), context.mock(FileResolver.class));
+        artifactPom = new DefaultArtifactPom(testPom);
+    }
+
+    @Test
+    public void pomWithMainArtifact() {
+        Artifact mainArtifact = createTestArtifact("someName", null, "mainPackaging");
+        File mainFile = new File("someFile");
+
+        artifactPom.addArtifact(mainArtifact, mainFile);
+
+        assertThat(artifactPom.getArtifact().getName(), equalTo("someName"));
+        assertThat(artifactPom.getArtifact().getExtension(), equalTo("mainPackaging"));
+        assertThat(artifactPom.getArtifact().getType(), equalTo("mainPackaging"));
+        assertThat(artifactPom.getArtifact().getClassifier(), nullValue());
+        assertThat(artifactPom.getArtifact().getFile(), equalTo(mainFile));
+
+        assertThat(artifactPom.getPom().getGroupId(), equalTo("org"));
+        assertThat(artifactPom.getPom().getArtifactId(), equalTo("someName"));
+        assertThat(artifactPom.getPom().getVersion(), equalTo("1.0"));
+        assertThat(artifactPom.getPom().getPackaging(), equalTo("mainPackaging"));
+    }
+
+    @Test
+    public void pomWithMainArtifactAndClassifierArtifacts() {
+        Artifact mainArtifact = createTestArtifact("someName", null, "mainPackaging");
+        File mainFile = new File("someFile");
+        Artifact classifierArtifact = createTestArtifact("otherName", "javadoc", "zip");
+        File classifierFile = new File("someClassifierFile");
+
+        artifactPom.addArtifact(mainArtifact, mainFile);
+        artifactPom.addArtifact(classifierArtifact, classifierFile);
+
+        assertThat(artifactPom.getArtifact().getName(), equalTo("someName"));
+        assertThat(artifactPom.getArtifact().getExtension(), equalTo("mainPackaging"));
+        assertThat(artifactPom.getArtifact().getType(), equalTo("mainPackaging"));
+        assertThat(artifactPom.getArtifact().getClassifier(), nullValue());
+        assertThat(artifactPom.getArtifact().getFile(), equalTo(mainFile));
+
+        PublishArtifact artifact = singleItem(artifactPom.getAttachedArtifacts());
+        assertThat(artifact.getName(), equalTo("someName"));
+        assertThat(artifact.getExtension(), equalTo("zip"));
+        assertThat(artifact.getType(), equalTo("zip"));
+        assertThat(artifact.getClassifier(), equalTo("javadoc"));
+        assertThat(artifact.getFile(), equalTo(classifierFile));
+
+        assertThat(artifactPom.getPom().getGroupId(), equalTo("org"));
+        assertThat(artifactPom.getPom().getArtifactId(), equalTo("someName"));
+        assertThat(artifactPom.getPom().getVersion(), equalTo("1.0"));
+        assertThat(artifactPom.getPom().getPackaging(), equalTo("mainPackaging"));
+    }
+
+    @Test
+    public void pomWithClassifierArtifactsOnly() {
+        File classifierFile = new File("someClassifierFile");
+        Artifact classifierArtifact = createTestArtifact("someName", "javadoc", "zip");
+
+        artifactPom.addArtifact(classifierArtifact, classifierFile);
+
+        assertThat(artifactPom.getArtifact(), nullValue());
+
+        PublishArtifact artifact = singleItem(artifactPom.getAttachedArtifacts());
+        assertThat(artifact.getName(), equalTo("someName"));
+        assertThat(artifact.getExtension(), equalTo("zip"));
+        assertThat(artifact.getType(), equalTo("zip"));
+        assertThat(artifact.getClassifier(), equalTo("javadoc"));
+        assertThat(artifact.getFile(), equalTo(classifierFile));
+
+        assertThat(artifactPom.getPom().getGroupId(), equalTo("org"));
+        assertThat(artifactPom.getPom().getArtifactId(), equalTo("someName"));
+        assertThat(artifactPom.getPom().getVersion(), equalTo("1.0"));
+        assertThat(artifactPom.getPom().getPackaging(), equalTo("jar"));
+    }
+
+    @Test
+    public void pomWithMainArtifactAndMetadataArtifacts() {
+        Artifact mainArtifact = createTestArtifact("someName", null, "mainPackaging");
+        File mainFile = new File("someFile");
+        File metadataFile = new File("someMetadataFile");
+        Artifact metadataArtifact = createTestArtifact("otherName", null, "sometype");
+
+        artifactPom.addArtifact(mainArtifact, mainFile);
+        artifactPom.addArtifact(metadataArtifact, metadataFile);
+
+        assertThat(artifactPom.getArtifact().getName(), equalTo("someName"));
+        assertThat(artifactPom.getArtifact().getExtension(), equalTo("mainPackaging"));
+        assertThat(artifactPom.getArtifact().getType(), equalTo("mainPackaging"));
+        assertThat(artifactPom.getArtifact().getClassifier(), nullValue());
+        assertThat(artifactPom.getArtifact().getFile(), equalTo(mainFile));
+
+        PublishArtifact artifact = singleItem(artifactPom.getAttachedArtifacts());
+        assertThat(artifact.getName(), equalTo("someName"));
+        assertThat(artifact.getExtension(), equalTo("sometype"));
+        assertThat(artifact.getType(), equalTo("sometype"));
+        assertThat(artifact.getClassifier(), nullValue());
+        assertThat(artifact.getFile(), equalTo(metadataFile));
+
+        assertThat(artifactPom.getPom().getGroupId(), equalTo("org"));
+        assertThat(artifactPom.getPom().getArtifactId(), equalTo("someName"));
+        assertThat(artifactPom.getPom().getVersion(), equalTo("1.0"));
+        assertThat(artifactPom.getPom().getPackaging(), equalTo("mainPackaging"));
+    }
+    
+    @Test(expected = InvalidUserDataException.class)
+    public void addClassifierTwiceShouldThrowInvalidUserDataEx() {
+        File classifierFile = new File("someClassifierFile");
+        Artifact classifierArtifact = createTestArtifact("someName", "javadoc");
+        artifactPom.addArtifact(classifierArtifact, classifierFile);
+        artifactPom.addArtifact(classifierArtifact, classifierFile);
+    }
+
+    @Test(expected = InvalidUserDataException.class)
+    public void addMainArtifactTwiceShouldThrowInvalidUserDataEx() {
+        Artifact mainArtifact = createTestArtifact("someName", null, "mainPackaging");
+        File mainFile = new File("someFile");
+        artifactPom.addArtifact(mainArtifact, mainFile);
+        artifactPom.addArtifact(mainArtifact, mainFile);
+    }
+
+    @Test
+    public void cannotAddMultipleArtifactsWithTheSameTypeAndClassifier() {
+
+        // No classifier
+        Artifact mainArtifact = createTestArtifact("someName", null);
+        artifactPom.addArtifact(mainArtifact, new File("someFile"));
+
+        assertIsDuplicate(mainArtifact, new File("someFile"));
+        assertIsDuplicate(mainArtifact, new File("otherFile"));
+        assertIsDuplicate(createTestArtifact("otherName", null), new File("otherFile"));
+
+        // Classifier
+        Artifact classifierArtifact = createTestArtifact("someName", "classifier");
+        artifactPom.addArtifact(classifierArtifact, new File("classifierFile"));
+
+        assertIsDuplicate(classifierArtifact, new File("someFile"));
+        assertIsDuplicate(classifierArtifact, new File("otherFile"));
+        assertIsDuplicate(createTestArtifact("otherName", "classifier"), new File("otherFile"));
+    }
+
+    private void assertIsDuplicate(Artifact artifact, File file) {
+        try {
+            artifactPom.addArtifact(artifact, file);
+            fail();
+        } catch (InvalidUserDataException e) {
+            assertThat(e.getMessage(), startsWith("A POM cannot have multiple artifacts with the same type and classifier."));
+        }
+    }
+
+    @Test
+    public void initWithCustomPomSettings() {
+        Artifact mainArtifact = createTestArtifact("someName", null, "mainPackaging");
+        File mainFile = new File("someFile");
+
+        testPom.setArtifactId("customArtifactId");
+        testPom.setGroupId("customGroupId");
+        testPom.setVersion("customVersion");
+        testPom.setPackaging("customPackaging");
+
+        artifactPom.addArtifact(mainArtifact, mainFile);
+
+        assertThat(artifactPom.getArtifact().getName(), equalTo("customArtifactId"));
+        assertThat(artifactPom.getArtifact().getExtension(), equalTo("mainPackaging"));
+        assertThat(artifactPom.getArtifact().getType(), equalTo("mainPackaging"));
+        assertThat(artifactPom.getArtifact().getClassifier(), nullValue());
+        assertThat(artifactPom.getArtifact().getFile(), equalTo(mainFile));
+
+        assertThat(artifactPom.getPom().getGroupId(), equalTo("customGroupId"));
+        assertThat(artifactPom.getPom().getArtifactId(), equalTo("customArtifactId"));
+        assertThat(artifactPom.getPom().getVersion(), equalTo("customVersion"));
+        assertThat(artifactPom.getPom().getPackaging(), equalTo("mainPackaging"));
+    }
+
+    private Artifact createTestArtifact(String name, String classifier) {
+        return createTestArtifact(name, classifier, "jar");
+    }
+
+    private Artifact createTestArtifact(String name, String classifier, String type) {
+        Map<String, String> extraAttributes = new HashMap<String, String>();
+        if (classifier != null) {
+            extraAttributes.put(Dependency.CLASSIFIER, classifier);
+        }
+        return new DefaultArtifact(ModuleRevisionId.newInstance("org", name, "1.0"), null, name, type, type, extraAttributes);
+    }
+
+    @Test
+    public void writePom() {
+        final MavenPom mavenPomMock = context.mock(MavenPom.class);
+        DefaultArtifactPom artifactPom = new DefaultArtifactPom(mavenPomMock);
+        final File somePomFile = new File(tmpDir.getDir(), "someDir/somePath");
+        context.checking(new Expectations() {{
+            allowing(mavenPomMock).getArtifactId();
+            will(returnValue("artifactId"));
+            one(mavenPomMock).writeTo(with(any(FileOutputStream.class)));
+        }});
+
+        PublishArtifact artifact = artifactPom.writePom(somePomFile);
+
+        assertThat(artifact.getName(), equalTo("artifactId"));
+        assertThat(artifact.getType(), equalTo("pom"));
+        assertThat(artifact.getExtension(), equalTo("pom"));
+        assertThat(artifact.getClassifier(), nullValue());
+        assertThat(artifact.getFile(), equalTo(somePomFile));
+    }
+
+    private <T> T singleItem(Iterable<? extends T> collection) {
+        List<T> elements = GUtil.addLists(collection);
+        assertThat(elements.size(), equalTo(1));
+        return elements.get(0);
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultPomFilterTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultPomFilterTest.java
similarity index 100%
rename from subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultPomFilterTest.java
rename to subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultPomFilterTest.java
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenDeployerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenDeployerTest.groovy
new file mode 100644
index 0000000..2d8b610
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenDeployerTest.groovy
@@ -0,0 +1,117 @@
+/*
+ * 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.PomFilterContainer
+import org.gradle.api.internal.artifacts.publish.maven.deploy.BaseMavenDeployer
+import org.gradle.api.internal.artifacts.publish.maven.deploy.BaseMavenDeployerTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import static org.junit.Assert.assertEquals
+
+/**
+ * @author Hans Dockter
+ */
+ at RunWith (org.jmock.integration.junit4.JMock.class)
+class DefaultGroovyMavenDeployerTest extends BaseMavenDeployerTest {
+    private DefaultGroovyMavenDeployer groovyMavenDeployer;
+
+    protected PomFilterContainer createPomFilterContainerMock() {
+        context.mock(PomFilterContainer.class);
+    }
+
+    protected BaseMavenDeployer createMavenDeployer() {
+        groovyMavenDeployer = new DefaultGroovyMavenDeployer(TEST_NAME, pomFilterContainerMock, artifactPomContainerMock, loggingManagerMock)
+    }
+
+    @Before
+    void setUp() {
+        super.setUp();
+    }
+
+    @Test
+    void repositoryBuilder() {
+        checkRepositoryBuilder(DefaultGroovyMavenDeployer.REPOSITORY_BUILDER)
+    }
+
+    @Test
+    void snapshotRepositoryBuilder() {
+        checkRepositoryBuilder(DefaultGroovyMavenDeployer.SNAPSHOT_REPOSITORY_BUILDER)
+    }
+
+
+    void checkRepositoryBuilder(String repositoryName) {
+        String testUrl = 'testUrl'
+        String testProxyHost = 'hans'
+        String testUserName = 'userId'
+        String testSnapshotUpdatePolicy = 'always'
+        String testReleaseUpdatePolicy = 'never'
+        groovyMavenDeployer."$repositoryName"(url: testUrl) {
+            authentication(userName: testUserName)
+            proxy(host: testProxyHost)
+            releases(updatePolicy: testReleaseUpdatePolicy)
+            snapshots(updatePolicy: testSnapshotUpdatePolicy)
+        }
+        assertEquals(testUrl, groovyMavenDeployer."$repositoryName".url)
+        assertEquals(testUserName, groovyMavenDeployer."$repositoryName".authentication.userName)
+        assertEquals(testProxyHost, groovyMavenDeployer."$repositoryName".proxy.host)
+        assertEquals(testReleaseUpdatePolicy, groovyMavenDeployer."$repositoryName".releases.updatePolicy)
+        assertEquals(testSnapshotUpdatePolicy, groovyMavenDeployer."$repositoryName".snapshots.updatePolicy)
+    }
+
+    @Test
+    void filter() {
+        Closure testClosure = {}
+        context.checking {
+            one(pomFilterContainerMock).filter(testClosure)
+        }
+        groovyMavenDeployer.filter(testClosure)
+    }
+
+    @Test
+    void pom() {
+        Closure testClosure = {}
+        context.checking {
+            one(pomFilterContainerMock).pom(testClosure)
+        }
+        groovyMavenDeployer.pom(testClosure)
+    }
+
+    @Test
+    void pomWithName() {
+        Closure testClosure = {}
+        String testName = 'somename'
+        context.checking {
+            one(pomFilterContainerMock).pom(testName, testClosure)
+        }
+        groovyMavenDeployer.pom(testName, testClosure)
+    }
+
+    @Test
+    void addFilter() {
+        Closure testClosure = {}
+        String testName = 'somename'
+        context.checking {
+            one(pomFilterContainerMock).addFilter(testName, testClosure)
+        }
+        groovyMavenDeployer.addFilter(testName, testClosure)
+    }
+}
+
+
+
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyPomFilterContainerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyPomFilterContainerTest.groovy
new file mode 100644
index 0000000..a40fa70
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyPomFilterContainerTest.groovy
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+
+
+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
+
+/**
+ * @author Hans Dockter
+ */
+ at RunWith(JMock)
+class DefaultGroovyPomFilterContainerTest extends BasePomFilterContainerTest {
+    static final String TEST_NAME = "somename"
+    PomFilterContainer groovyPomFilterContainer
+
+    @Before
+    public void setUp() {
+        super.setUp()
+    }
+
+    protected BasePomFilterContainer createPomFilterContainer() {
+        return groovyPomFilterContainer = new BasePomFilterContainer(mavenPomFactoryMock);
+    }
+
+    @Test
+    public void addFilterWithClosure() {
+        Closure closureFilter = {}
+        MavenPom pom = groovyPomFilterContainer.addFilter(TEST_NAME, closureFilter)
+        assertSame(pomMock, pom);
+        assertSame(pomMock, groovyPomFilterContainer.pom(TEST_NAME));
+        assertSame(closureFilter, getClosureFromProxy(groovyPomFilterContainer.filter(TEST_NAME)));
+    }
+
+    private Closure getClosureFromProxy(PublishFilter filter) {
+        Proxy.getInvocationHandler(filter).delegate
+    }
+
+    @Test
+    public void filterWithClosure() {
+        Closure closureFilter = {}
+        context.checking {
+            one(pomFilterMock).setFilter(withParam(FilterMatcher.equalsFilter(closureFilter)))
+        }
+        groovyPomFilterContainer.filter(closureFilter)
+    }
+
+    @Test
+    public void defaultPomWithClosure() {
+        String testGroup = "testGroup"
+        context.checking {
+            one(pomFilterMock).getPomTemplate(); will(returnValue(pomMock))
+            one(pomMock).setGroupId(testGroup);
+        }
+        groovyPomFilterContainer.pom {
+            groupId = testGroup
+        }
+    }
+
+    @Test
+    public void pomWithClosure() {
+        groovyPomFilterContainer.addFilter(TEST_NAME, {})
+        String testGroup = "testGroup"
+        context.checking {
+            one(pomMock).setGroupId(testGroup);
+        }
+        groovyPomFilterContainer.pom(TEST_NAME) {
+            groupId = testGroup
+        }
+    }
+}
+
+public class FilterMatcher extends BaseMatcher {
+    Closure filter
+
+    public void describeTo(Description description) {
+        description.appendText("matching filter");
+    }
+
+    public boolean matches(Object actual) {
+        return getClosureFromProxy(actual) == filter;
+    }
+
+    private Closure getClosureFromProxy(PublishFilter filter) {
+        Proxy.getInvocationHandler(filter).delegate
+    }
+
+
+    @Factory
+    public static Matcher<PublishFilter> equalsFilter(Closure filter) {
+        return new FilterMatcher(filter: filter);
+    }
+
+}
+
+
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/mvnsettings/EmptyMavenSettingsSupplierTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/mvnsettings/EmptyMavenSettingsSupplierTest.groovy
new file mode 100644
index 0000000..9494388
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/mvnsettings/EmptyMavenSettingsSupplierTest.groovy
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.mvnsettings
+
+import org.apache.maven.artifact.ant.InstallDeployTaskSupport
+import spock.lang.Specification
+
+/**
+ * Author: Szczepan Faber, created at: 3/29/11
+ */
+class EmptyMavenSettingsSupplierTest extends Specification {
+
+    def EmptyMavenSettingsSupplier supplier = new EmptyMavenSettingsSupplier()
+    InstallDeployTaskSupport support = Mock()
+
+    def "supplies empty settings"() {
+        when:
+        supplier.supply(support)
+
+        then:
+        supplier.settingsXml.text == '<settings/>'
+//        1 * support.setSettingsFile(supplier.settingsXml) //not sure why it doesn't work
+    }
+
+    def "deletes file when done"() {
+        when:
+        supplier.supply(support)
+        supplier.done()
+
+        then:
+        !supplier.settingsXml.exists()
+    }
+
+    def "done operation must be safe"() {
+        when:
+        supplier.done()
+
+        then:
+        noExceptionThrown()
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/mvnsettings/MaybeUserMavenSettingsSupplierTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/mvnsettings/MaybeUserMavenSettingsSupplierTest.groovy
new file mode 100644
index 0000000..5a447c1
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/mvnsettings/MaybeUserMavenSettingsSupplierTest.groovy
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.mvnsettings
+
+import org.apache.maven.artifact.ant.InstallDeployTaskSupport
+import spock.lang.Specification
+
+/**
+ * Author: Szczepan Faber, created at: 3/29/11
+ */
+class MaybeUserMavenSettingsSupplierTest extends Specification {
+
+    InstallDeployTaskSupport support = Mock()
+    def supplier = new MaybeUserMavenSettingsSupplier()
+
+    def "supplies empty settings when user settings not found"() {
+        given:
+        supplier.emptySettingsSupplier = Mock(EmptyMavenSettingsSupplier)
+        supplier.mavenSettingsProvider = Mock(MavenSettingsProvider)
+
+        supplier.mavenSettingsProvider.getUserSettingsFile() >> { new File('does not exist') }
+
+        when:
+        supplier.supply(support)
+        supplier.done()
+
+        then:
+        1 * supplier.emptySettingsSupplier.supply(support)
+        1 * supplier.emptySettingsSupplier.done()
+    }
+
+    def "supplies user settings when file exists"() {
+        given:
+        supplier.emptySettingsSupplier = Mock(EmptyMavenSettingsSupplier)
+        supplier.mavenSettingsProvider = Mock(MavenSettingsProvider)
+
+        def concreteFile = File.createTempFile('I exist', ', really')
+        concreteFile.deleteOnExit()
+        supplier.mavenSettingsProvider.getUserSettingsFile() >> { concreteFile }
+
+        when:
+        supplier.supply(support)
+        supplier.done()
+
+        then:
+        1 * support.setSettingsFile(concreteFile)
+        0 * supplier.emptySettingsSupplier.supply(support)
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultIvyArtifactRepositoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultIvyArtifactRepositoryTest.groovy
new file mode 100644
index 0000000..0906c77
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultIvyArtifactRepositoryTest.groovy
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 spock.lang.Specification
+import org.apache.ivy.plugins.resolver.URLResolver
+import org.apache.ivy.plugins.resolver.RepositoryResolver
+
+class DefaultIvyArtifactRepositoryTest extends Specification {
+    final DefaultIvyArtifactRepository repository = new DefaultIvyArtifactRepository()
+
+    def createsAUrlResolver() {
+        repository.name = 'name'
+        repository.artifactPattern 'pattern'
+
+        when:
+        def resolvers = []
+        repository.createResolvers(resolvers)
+
+        then:
+        resolvers.size() == 1
+        def resolver = resolvers[0]
+        resolver instanceof URLResolver
+        resolver.name == 'name'
+        resolver.artifactPatterns == ['pattern'] as List
+    }
+
+    def createsARepositoryResolverForHttpPatterns() {
+        repository.name = 'name'
+        repository.artifactPattern 'http://host/[organisation]/[artifact]-[revision].[ext]'
+
+        when:
+        def resolvers = []
+        repository.createResolvers(resolvers)
+
+        then:
+        resolvers.size() == 1
+        def resolver = resolvers[0]
+        resolver instanceof RepositoryResolver
+        resolver.repository instanceof CommonsHttpClientBackedRepository
+        resolver.name == 'name'
+        resolver.artifactPatterns == ['http://host/[organisation]/[artifact]-[revision].[ext]'] as List
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultResolverFactoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultResolverFactoryTest.groovy
new file mode 100644
index 0000000..efead5d
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultResolverFactoryTest.groovy
@@ -0,0 +1,127 @@
+/*
+ * 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.internal.artifacts.repositories
+
+import org.gradle.api.InvalidUserDataException
+import org.gradle.api.artifacts.ResolverContainer
+import org.gradle.api.artifacts.maven.MavenFactory
+import org.gradle.api.internal.Factory
+import org.gradle.api.internal.artifacts.ivyservice.GradleIBiblioResolver
+import org.gradle.api.internal.artifacts.publish.maven.LocalMavenCacheLocator
+import org.gradle.util.JUnit4GroovyMockery
+import org.jmock.integration.junit4.JMock
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.apache.ivy.plugins.resolver.*
+
+/**
+ * @author Hans Dockter
+ */
+ at RunWith(JMock.class)
+class DefaultResolverFactoryTest {
+    static final String RESOLVER_URL = 'http://a.b.c/'
+    static final Map RESOLVER_MAP = [name: 'mapresolver', url: 'http://x.y.z/']
+    static final IBiblioResolver TEST_RESOLVER = new IBiblioResolver()
+    static {
+        TEST_RESOLVER.name = 'ivyResolver'
+    }
+
+    static final String TEST_REPO_NAME = 'reponame'
+    static final String TEST_REPO_URL = 'http://www.gradle.org'
+    static final File TEST_CACHE_DIR = 'somepath' as File
+
+    final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
+    final LocalMavenCacheLocator localMavenCacheLocator = context.mock(LocalMavenCacheLocator.class)
+    final DefaultResolverFactory factory = new DefaultResolverFactory(context.mock(Factory.class), context.mock(MavenFactory.class), localMavenCacheLocator)
+
+    @Test(expected = InvalidUserDataException) public void testCreateResolver() {
+        checkMavenResolver(factory.createResolver(RESOLVER_URL), RESOLVER_URL, RESOLVER_URL)
+        checkMavenResolver(factory.createResolver(RESOLVER_MAP), RESOLVER_MAP.name, RESOLVER_MAP.url)
+        DependencyResolver resolver = factory.createResolver(TEST_RESOLVER)
+        assert resolver.is(TEST_RESOLVER)
+        def someIllegalDescription = new NullPointerException()
+        factory.createResolver(someIllegalDescription)
+    }
+
+    private void checkMavenResolver(IBiblioResolver resolver, String name, String url) {
+        assert url == resolver.root
+        assert name == resolver.name
+        assert resolver.allownomd
+    }
+
+    @Test
+    public void testCreateMavenRepoWithAdditionalJarUrls() {
+        String testUrl2 = 'http://www.gradle2.org'
+        DualResolver dualResolver = factory.createMavenRepoResolver(TEST_REPO_NAME, TEST_REPO_URL, testUrl2)
+        assert dualResolver.allownomd
+        checkIBiblio(dualResolver.ivyResolver, "_poms")
+        URLResolver urlResolver = dualResolver.artifactResolver
+        assert urlResolver.m2compatible
+        assert urlResolver.artifactPatterns.contains("$TEST_REPO_URL/$ResolverContainer.MAVEN_REPO_PATTERN" as String)
+        assert urlResolver.artifactPatterns.contains("$testUrl2/$ResolverContainer.MAVEN_REPO_PATTERN" as String)
+        assert "${TEST_REPO_NAME}_jars" == urlResolver.name
+    }
+
+    @Test
+    public void testCreateMavenRepoWithNoAdditionalJarUrls() {
+        checkIBiblio(factory.createMavenRepoResolver(TEST_REPO_NAME, TEST_REPO_URL), "")
+    }
+
+    private void checkIBiblio(IBiblioResolver iBiblioResolver, String expectedNameSuffix) {
+        assert iBiblioResolver.usepoms
+        assert iBiblioResolver.m2compatible
+        assert iBiblioResolver.allownomd
+        assert TEST_REPO_URL + '/' == iBiblioResolver.root
+        assert ResolverContainer.MAVEN_REPO_PATTERN == iBiblioResolver.pattern
+        assert "${TEST_REPO_NAME}$expectedNameSuffix" == iBiblioResolver.name
+    }
+
+    @Test public void testCreateFlatDirResolver() {
+        File dir1 = new File('/rootFolder')
+        File dir2 = new File('/rootFolder2')
+        String expectedName = 'libs'
+        FileSystemResolver resolver = factory.createFlatDirResolver(expectedName, [dir1, dir2] as File[])
+        def expectedPatterns = [
+                "$dir1.absolutePath/[artifact]-[revision](-[classifier]).[ext]",
+                "$dir1.absolutePath/[artifact](-[classifier]).[ext]",
+                "$dir2.absolutePath/[artifact]-[revision](-[classifier]).[ext]",
+                "$dir2.absolutePath/[artifact](-[classifier]).[ext]"
+        ]
+        assert expectedName == resolver.name
+        assert [] == resolver.ivyPatterns
+        assert expectedPatterns == resolver.artifactPatterns
+        assert resolver.allownomd
+    }
+
+    @Test public void testCreateLocalMavenRepo() {
+        File repoDir = new File(".m2/repository")
+
+        context.checking {
+            one(localMavenCacheLocator).getLocalMavenCache()
+            will(returnValue(repoDir))
+        }
+
+        def repo = factory.createMavenLocalResolver('name')
+        assert repo instanceof GradleIBiblioResolver
+        assert repo.root == repoDir.toURI().toString() + '/'
+    }
+
+    @Test public void createIvyRepository() {
+        def repo = factory.createIvyRepository()
+        assert repo instanceof DefaultIvyArtifactRepository
+    }
+}
diff --git a/subprojects/core/core.gradle b/subprojects/core/core.gradle
index 7473e3c..caba001 100644
--- a/subprojects/core/core.gradle
+++ b/subprojects/core/core.gradle
@@ -40,41 +40,37 @@ dependencies {
 
     compile libraries.ivy, "com.jcraft:jsch:0.1.42 at jar", 'com.jcraft:jzlib:1.0.7 at jar'
 
+    publishCompile libraries.slf4j_api
+
     compile libraries.ant,
-            libraries.ant_nodeps,
             libraries.logback_classic,
             libraries.logback_core,
-            libraries.slf4j_api,
             libraries.jul_to_slf4j,
             libraries.commons_io,
             libraries.commons_lang,
             "commons-codec:commons-codec:1.2 at jar",
-            libraries.google_collections,
+            libraries.guava,
             "commons-collections:commons-collections:3.2.1 at jar",
             "slide:webdavlib:2.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',
-            'org.sonatype.pmaven:pmaven-common:0.8-20100325 at jar',
-            'org.sonatype.pmaven:pmaven-groovy:0.8-20100325 at jar',
-            'org.codehaus.plexus:plexus-component-annotations:1.5.2'
+            'org.jruby.ext.posix:jna-posix:1.0.3'
 
     runtime 'net.java.dev.jna:jna:3.2.2'
 
     runtime libraries.log4j_to_slf4j, libraries.jcl_to_slf4j
 
+    // tried with scope 'runtime' but this gives a cyclic task dependency
+    testRuntime project(":coreImpl")
+
     testCompile libraries.xmlunit
 
     compile libraries.ant_launcher
 
-    integTestCompile libraries.jetty_depends, project(':wrapper'), project(':toolingApi')
+    integTestCompile libraries.jetty_depends
 
     testFixtures sourceSets.test.classes, sourceSets.main.classes
     integTestFixtures sourceSets.integTest.classes
-
-    // TODO - remove this duplication
-    publishRuntime libraries.slf4j_api
 }
 
 task versionProperties(type: WriteVersionProperties) {
@@ -97,10 +93,6 @@ ideaModule {
 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))))
-	whenConfigured { classpath ->
-		classpath.entries.removeAll { entry -> entry.kind == 'src' && entry.path.startsWith('src/integTest/resources')}
-	}
 }
 
 [compileGroovy, compileTestGroovy]*.groovyOptions*.fork(memoryInitialSize: '128M', memoryMaximumSize: '1G')
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/AbstractIdeIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/AbstractIdeIntegrationTest.groovy
deleted file mode 100644
index 6c6dd3f..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/AbstractIdeIntegrationTest.groovy
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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
-
-abstract class AbstractIdeIntegrationTest extends AbstractIntegrationTest {
-    protected parseXmlFile(filename, print) {
-        def file = file(filename).assertExists()
-        if (print) { println file.text }
-        new XmlSlurper().parse(file)
-    }
-
-    protected publishArtifact(dir, group, artifact, dependency) {
-        def artifactDir = new File("$dir/$group/$artifact/1.0")
-        assert artifactDir.mkdirs()
-
-        def pomFile = new File("$artifactDir/$artifact-1.0.pom")
-        pomFile << """
-<project xmlns="http://maven.apache.org/POM/4.0.0">
-  <modelVersion>4.0.0</modelVersion>
-  <groupId>$group</groupId>
-  <artifactId>$artifact</artifactId>
-  <packaging>jar</packaging>
-  <version>1.0</version>
-
-  <dependencies>
-    <dependency>
-      <groupId>$group</groupId>
-      <artifactId>$dependency</artifactId>
-      <version>1.0</version>
-    </dependency>
-  </dependencies>
-</project>
-        """
-
-        def jarFile = new File("$artifactDir/$artifact-1.0.jar")
-        jarFile << "add some content so that file size isn't zero"
-
-        jarFile
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/AbstractIntegrationTest.java b/subprojects/core/src/integTest/groovy/org/gradle/integtests/AbstractIntegrationTest.java
deleted file mode 100644
index 320a921..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/AbstractIntegrationTest.java
+++ /dev/null
@@ -1,77 +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.CacheUsage;
-import org.gradle.StartParameter;
-import org.gradle.integtests.fixtures.*;
-import org.gradle.util.TestFile;
-import org.gradle.util.TestFileContext;
-import org.junit.Rule;
-
-import java.io.File;
-
-public abstract class AbstractIntegrationTest implements TestFileContext {
-    @Rule public final GradleDistribution distribution = new GradleDistribution();
-    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter();
-
-    public TestFile getTestDir() {
-        return distribution.getTestDir();
-    }
-
-    public TestFile file(Object... path) {
-        return getTestDir().file(path);
-    }
-
-    public TestFile testFile(String name) {
-        return file(name);
-    }
-
-    private StartParameter startParameter() {
-        StartParameter parameter = new StartParameter();
-        parameter.setGradleUserHomeDir(distribution.getUserHomeDir());
-
-        parameter.setSearchUpwards(false);
-        parameter.setCacheUsage(CacheUsage.ON);
-        parameter.setCurrentDir(getTestDir());
-
-        return parameter;
-    }
-
-    protected GradleExecuter inTestDirectory() {
-        return inDirectory(getTestDir());
-    }
-
-    protected GradleExecuter inDirectory(File directory) {
-        return executer.inDirectory(directory);
-    }
-
-    protected GradleExecuter usingBuildFile(File file) {
-        return executer.usingBuildScript(file);
-    }
-
-    protected GradleExecuter usingBuildScript(String script) {
-        return executer.usingBuildScript(script);
-    }
-
-    protected GradleExecuter usingProjectDir(File projectDir) {
-        return executer.usingProjectDirectory(projectDir);
-    }
-
-    protected ArtifactBuilder artifactBuilder() {
-        return new GradleBackedArtifactBuilder(new InProcessGradleExecuter(startParameter()), getTestDir().file("artifacts"));
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/AntProjectIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/AntProjectIntegrationTest.groovy
deleted file mode 100644
index 7f29fd8..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/AntProjectIntegrationTest.groovy
+++ /dev/null
@@ -1,158 +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.integtests.fixtures.ExecutionFailure
-import org.gradle.util.TestFile
-import org.junit.Test
-import static org.hamcrest.Matchers.*
-
-public class AntProjectIntegrationTest extends AbstractIntegrationTest {
-    @Test
-    public void antTargetsAndGradleTasksCanDependOnEachOther() {
-        testFile('build.xml') << """
-<project>
-    <target name='target1' depends='target2,init'>
-        <touch file='build/target1.txt'/>
-    </target>
-    <target name='target2' depends='init'>
-        <touch file='build/target2.txt'/>
-    </target>
-</project>
-"""
-        testFile('build.gradle') << """
-ant.importBuild(file('build.xml'))
-task init << { buildDir.mkdirs() }
-task ant(dependsOn: target1)
-"""
-        TestFile target1File = testFile('build/target1.txt')
-        TestFile target2File = testFile('build/target2.txt')
-        target1File.assertDoesNotExist()
-        target2File.assertDoesNotExist()
-
-        inTestDirectory().withTasks('ant').run().assertTasksExecuted(':init', ':target2', ':target1', ':ant')
-
-        target1File.assertExists()
-        target2File.assertExists()
-    }
-
-    @Test
-    public void canImportMultipleBuildFilesWithDifferentBaseDirs() {
-        testFile('project1/build.xml') << """
-<project>
-    <target name='target1'>
-        <mkdir dir='build'/>
-        <touch file='build/target1.txt'/>
-    </target>
-</project>
-"""
-        testFile('project2/build.xml') << """
-<project>
-    <target name='target2'>
-        <mkdir dir='build'/>
-        <touch file='build/target2.txt'/>
-    </target>
-</project>
-"""
-        testFile('build.gradle') << """
-ant.importBuild('project1/build.xml')
-ant.importBuild('project2/build.xml')
-task ant(dependsOn: [target1, target2])
-"""
-        TestFile target1File = testFile('project1/build/target1.txt')
-        TestFile target2File = testFile('project2/build/target2.txt')
-        target1File.assertDoesNotExist()
-        target2File.assertDoesNotExist()
-
-        inTestDirectory().withTasks('ant').run().assertTasksExecuted(':target1', ':target2', ':ant')
-
-        target1File.assertExists()
-        target2File.assertExists()
-    }
-
-    @Test
-    public void handlesAntImportsOk() {
-        testFile('imported.xml') << """
-<project>
-    <target name='target1'>
-        <mkdir dir='build'/>
-        <touch file='build/target1.txt'/>
-    </target>
-</project>
-"""
-        testFile('build.xml') << """
-<project>
-    <import file="imported.xml"/>
-    <target name='target2'>
-        <mkdir dir='build'/>
-        <touch file='build/target2.txt'/>
-    </target>
-</project>
-"""
-        testFile('build.gradle') << """
-ant.importBuild('build.xml')
-task ant(dependsOn: [target1, target2])
-"""
-        TestFile target1File = testFile('build/target1.txt')
-        TestFile target2File = testFile('build/target2.txt')
-        target1File.assertDoesNotExist()
-        target2File.assertDoesNotExist()
-
-        inTestDirectory().withTasks('ant').run().assertTasksExecuted(':target1', ':target2', ':ant')
-
-        target1File.assertExists()
-        target2File.assertExists()
-    }
-
-    @Test
-    public void reportsAntBuildParseFailure() {
-        TestFile antBuildFile = testFile('build.xml')
-        antBuildFile << """
-<project>
-    <target name='target1'
-        <unknown/>
-    </target>
-</project>
-"""
-        TestFile buildFile = testFile('build.gradle')
-        buildFile << """
-ant.importBuild('build.xml')
-"""
-        ExecutionFailure failure = inTestDirectory().withTasks('target1').runWithFailure()
-        failure.assertHasFileName("Build file '$buildFile'")
-        failure.assertThatDescription(startsWith('A problem occurred evaluating root project'))
-        failure.assertHasCause("Could not import Ant build file '$antBuildFile'.")
-    }
-
-    @Test
-    public void reportsAntTaskExecutionFailure() {
-        testFile('build.xml') << """
-<project>
-    <target name='target1'>
-        <fail>broken</fail>
-    </target>
-</project>
-"""
-        TestFile buildFile = testFile('build.gradle')
-        buildFile << """
-ant.importBuild('build.xml')
-"""
-        ExecutionFailure failure = inTestDirectory().withTasks('target1').runWithFailure()
-        failure.assertHasFileName("Build file '$buildFile'")
-        failure.assertHasDescription('Execution failed for task \':target1\'.')
-        failure.assertHasCause('broken')
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/AntlrIntegrationTest.java b/subprojects/core/src/integTest/groovy/org/gradle/integtests/AntlrIntegrationTest.java
deleted file mode 100644
index d35f052..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/AntlrIntegrationTest.java
+++ /dev/null
@@ -1,26 +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.junit.Test;
-
-public class AntlrIntegrationTest extends AbstractIntegrationTest {
-    @Test
-    public void handlesEmptyProject() {
-        testFile("build.gradle").writelns("apply plugin: 'antlr'");
-        inTestDirectory().withTasks("build").run();
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/ArchiveIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/ArchiveIntegrationTest.groovy
deleted file mode 100644
index 699b042..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/ArchiveIntegrationTest.groovy
+++ /dev/null
@@ -1,661 +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.util.TestFile
-import org.junit.Test
-import static org.junit.Assert.*
-import static org.hamcrest.Matchers.*
-
-public class ArchiveIntegrationTest extends AbstractIntegrationTest {
-    @Test public void canCopyFromAZip() {
-        createZip('test.zip') {
-            subdir1 {
-                file 'file1.txt'
-            }
-            subdir2 {
-                file 'file2.txt'
-                file 'file2.xml'
-            }
-        }
-
-        testFile('build.gradle') << '''
-            task copy(type: Copy) {
-                from zipTree('test.zip')
-                exclude '**/*.xml'
-                into 'dest'
-            }
-'''
-
-        inTestDirectory().withTasks('copy').run()
-
-        testFile('dest').assertHasDescendants('subdir1/file1.txt', 'subdir2/file2.txt')
-    }
-
-    @Test public void canCopyFromATar() {
-        createTar('test.tar') {
-            subdir1 {
-                file 'file1.txt'
-            }
-            subdir2 {
-                file 'file2.txt'
-                file 'file2.xml'
-            }
-        }
-
-        testFile('build.gradle') << '''
-            task copy(type: Copy) {
-                from tarTree('test.tar')
-                exclude '**/*.xml'
-                into 'dest'
-            }
-'''
-
-        inTestDirectory().withTasks('copy').run()
-
-        testFile('dest').assertHasDescendants('subdir1/file1.txt', 'subdir2/file2.txt')
-    }
-
-    @Test public void cannotCreateAnEmptyZip() {
-        testFile('build.gradle') << '''
-            task zip(type: Zip) {
-                from 'test'
-                destinationDir = buildDir
-                archiveName = 'test.zip'
-}
-'''
-
-        inTestDirectory().withTasks('zip').run()
-
-        testFile('build/test.zip').assertDoesNotExist()
-    }
-
-    @Test public void canCreateAnEmptyJar() {
-        testFile('build.gradle') << '''
-            task jar(type: Jar) {
-                from 'test'
-                destinationDir = buildDir
-                archiveName = 'test.jar'
-}
-'''
-
-        inTestDirectory().withTasks('jar').run()
-
-        TestFile expandDir = testFile('expanded')
-        testFile('build/test.jar').unzipTo(expandDir)
-        expandDir.assertHasDescendants('META-INF/MANIFEST.MF')
-
-        expandDir.file('META-INF/MANIFEST.MF').assertContents(equalTo('Manifest-Version: 1.0\r\n\r\n'))
-    }
-
-    @Test public void cannotCreateAnEmptyTar() {
-        testFile('build.gradle') << '''
-            task tar(type: Tar) {
-                from 'test'
-                destinationDir = buildDir
-                archiveName = 'test.tar'
-}
-'''
-
-        inTestDirectory().withTasks('tar').run()
-
-        testFile('build/test.tar').assertDoesNotExist()
-    }
-
-    @Test public void canCreateAZipArchive() {
-        createDir('test') {
-            dir1 {
-                file('file1.txt').write("abc")
-            }
-            file 'file1.txt'
-            dir2 {
-                file 'file2.txt'
-                file 'script.sh'
-            }
-        }
-
-        testFile('build.gradle') << '''
-            task zip(type: Zip) {
-                into('prefix') {
-                    from 'test'
-                    include '**/*.txt'
-                    rename { "renamed_$it" }
-                    filter { "[$it]" }
-                }
-                into('scripts') {
-                    from 'test'
-                    include '**/*.sh'
-                    dirMode = 0750
-                    fileMode = 0754
-                }
-                destinationDir = buildDir
-                archiveName = 'test.zip'
-            }
-'''
-
-        inTestDirectory().withTasks('zip').run()
-
-        TestFile expandDir = testFile('expanded')
-        testFile('build/test.zip').usingNativeTools().unzipTo(expandDir)
-        expandDir.assertHasDescendants(
-                'prefix/dir1/renamed_file1.txt',
-                'prefix/renamed_file1.txt',
-                'prefix/dir2/renamed_file2.txt',
-                'scripts/dir2/script.sh')
-
-        expandDir.file('prefix/dir1/renamed_file1.txt').assertContents(equalTo('[abc]'))
-
-        expandDir.file('prefix/dir1').assertPermissions(equalTo("rwxr-xr-x"))
-        expandDir.file('prefix/dir1/renamed_file1.txt').assertPermissions(equalTo("rw-r--r--"))
-        expandDir.file('scripts/dir2').assertPermissions(equalTo("rwxr-x---"))
-        expandDir.file('scripts/dir2/script.sh').assertPermissions(equalTo("rwxr-xr--"))
-    }
-
-    @Test public void canCreateATarArchive() {
-        createDir('test') {
-            dir1 {
-                file('file1.txt') << 'abc'
-            }
-            file 'file1.txt'
-            dir2 {
-                file 'file2.txt'
-                file 'script.sh'
-            }
-        }
-
-        testFile('build.gradle') << '''
-            task tar(type: Tar) {
-                from('test') {
-                    include '**/*.txt'
-                    filter { "[$it]" }
-                }
-                from('test') {
-                    include '**/*.sh'
-                    into 'scripts'
-                    fileMode = 0754
-                    dirMode = 0750
-                }
-                destinationDir = buildDir
-                archiveName = 'test.tar'
-            }
-'''
-
-        inTestDirectory().withTasks('tar').run()
-
-        TestFile expandDir = testFile('expanded')
-        testFile('build/test.tar').usingNativeTools().untarTo(expandDir)
-        expandDir.assertHasDescendants('dir1/file1.txt', 'file1.txt', 'dir2/file2.txt', 'scripts/dir2/script.sh')
-
-        expandDir.file('dir1/file1.txt').assertContents(equalTo('[abc]'))
-
-        expandDir.file('dir1').assertPermissions(equalTo("rwxr-xr-x"))
-        expandDir.file('dir1/file1.txt').assertPermissions(equalTo("rw-r--r--"))
-        expandDir.file('scripts/dir2').assertPermissions(equalTo("rwxr-x---"))
-        expandDir.file('scripts/dir2/script.sh').assertPermissions(equalTo("rwxr-xr--"))
-    }
-
-    @Test public void canCreateATgzArchive() {
-        createDir('test') {
-            dir1 {
-                file 'file1.txt'
-            }
-            file 'file1.txt'
-            dir2 {
-                file 'file2.txt'
-                file 'ignored.xml'
-            }
-        }
-
-        testFile('build.gradle') << '''
-            task tar(type: Tar) {
-                compression = Compression.GZIP
-                from 'test'
-                include '**/*.txt'
-                destinationDir = buildDir
-                archiveName = 'test.tgz'
-            }
-'''
-
-        inTestDirectory().withTasks('tar').run()
-
-        TestFile expandDir = testFile('expanded')
-        testFile('build/test.tgz').untarTo(expandDir)
-        expandDir.assertHasDescendants('dir1/file1.txt', 'file1.txt', 'dir2/file2.txt')
-    }
-
-    @Test public void canCreateATbzArchive() {
-        createDir('test') {
-            dir1 {
-                file 'file1.txt'
-            }
-            file 'file1.txt'
-            dir2 {
-                file 'file2.txt'
-                file 'ignored.xml'
-            }
-        }
-
-        testFile('build.gradle') << '''
-            task tar(type: Tar) {
-                compression = Compression.BZIP2
-                from 'test'
-                include '**/*.txt'
-                destinationDir = buildDir
-                archiveName = 'test.tbz2'
-            }
-'''
-
-        inTestDirectory().withTasks('tar').run()
-
-        TestFile expandDir = testFile('expanded')
-        testFile('build/test.tbz2').untarTo(expandDir)
-        expandDir.assertHasDescendants('dir1/file1.txt', 'file1.txt', 'dir2/file2.txt')
-    }
-
-    @Test public void canCreateAJarArchiveWithDefaultManifest() {
-        createDir('test') {
-            dir1 {
-                file 'file1.txt'
-            }
-        }
-        createDir('meta-inf') {
-            file 'file1.txt'
-            dir2 {
-                file 'file2.txt'
-            }
-        }
-
-        testFile('build.gradle') << '''
-            task jar(type: Jar) {
-                from 'test'
-                metaInf {
-                    from 'meta-inf'
-                }
-                destinationDir = buildDir
-                archiveName = 'test.jar'
-            }
-'''
-
-        inTestDirectory().withTasks('jar').run()
-
-        TestFile expandDir = testFile('expanded')
-        testFile('build/test.jar').unzipTo(expandDir)
-        expandDir.assertHasDescendants('META-INF/MANIFEST.MF', 'META-INF/file1.txt', 'META-INF/dir2/file2.txt', 'dir1/file1.txt')
-
-        expandDir.file('META-INF/MANIFEST.MF').assertContents(equalTo('Manifest-Version: 1.0\r\n\r\n'))
-    }
-
-    @Test public void metaInfSpecsAreIndependentOfOtherSpec() {
-        createDir('test') {
-            dir1 {
-                file 'ignored.xml'
-                file 'file1.txt'
-            }
-        }
-        createDir('meta-inf') {
-            dir2 {
-                file 'ignored.txt'
-                file 'file2.xml'
-            }
-        }
-        createDir('meta-inf2') {
-            file 'file2.txt'
-            file 'file2.xml'
-        }
-
-        testFile('build.gradle') << '''
-            task jar(type: Jar) {
-                from 'test'
-                include '**/*.txt'
-                metaInf {
-                    from 'meta-inf'
-                    include '**/*.xml'
-                }
-                metaInf {
-                    from 'meta-inf2'
-                    into 'dir3'
-                }
-                destinationDir = buildDir
-                archiveName = 'test.jar'
-            }
-'''
-
-        inTestDirectory().withTasks('jar').run()
-
-        TestFile expandDir = testFile('expanded')
-        testFile('build/test.jar').unzipTo(expandDir)
-        expandDir.assertHasDescendants(
-                'META-INF/MANIFEST.MF',
-                'META-INF/dir2/file2.xml',
-                'META-INF/dir3/file2.txt',
-                'META-INF/dir3/file2.xml',
-                'dir1/file1.txt')
-    }
-
-    @Test public void canCreateAWarArchiveWithNoWebXml() {
-        createDir('content') {
-            content1 {
-                file 'file1.jsp'
-            }
-        }
-        createDir('web-inf') {
-            webinf1 {
-                file 'file1.txt'
-            }
-        }
-        createDir('meta-inf') {
-            metainf1 {
-                file 'file2.txt'
-            }
-        }
-        createDir('classes') {
-            org {
-                gradle {
-                    file 'resource.txt'
-                    file 'Person.class'
-                }
-            }
-        }
-        createZip("lib.jar") {
-            file "Dependency.class"
-        }
-
-        testFile('build.gradle') << '''
-            task war(type: War) {
-                from 'content'
-                metaInf {
-                    from 'meta-inf'
-                }
-                webInf {
-                    from 'web-inf'
-                }
-                classpath 'classes'
-                classpath 'lib.jar'
-                destinationDir = buildDir
-                archiveName = 'test.war'
-            }
-'''
-
-        inTestDirectory().withTasks('war').run()
-
-        TestFile expandDir = testFile('expanded')
-        testFile('build/test.war').unzipTo(expandDir)
-        expandDir.assertHasDescendants(
-                'META-INF/MANIFEST.MF',
-                'META-INF/metainf1/file2.txt',
-                'content1/file1.jsp',
-                'WEB-INF/lib/lib.jar',
-                'WEB-INF/classes/org/gradle/resource.txt',
-                'WEB-INF/classes/org/gradle/Person.class',
-                'WEB-INF/webinf1/file1.txt')
-
-        expandDir.file('META-INF/MANIFEST.MF').assertContents(equalTo('Manifest-Version: 1.0\r\n\r\n'))
-    }
-
-    @Test public void canCreateAWarArchiveWithWebXml() {
-        testFile('some.xml') << '<web/>'
-        createDir('web-inf') {
-            webinf1 {
-                file 'file1.txt'
-            }
-        }
-
-        testFile('build.gradle') << '''
-            task war(type: War) {
-                webInf {
-                    from 'web-inf'
-                    exclude '**/*.xml'
-                }
-                webXml = file('some.xml')
-                destinationDir = buildDir
-                archiveName = 'test.war'
-            }
-'''
-
-        inTestDirectory().withTasks('war').run()
-
-        TestFile expandDir = testFile('expanded')
-        testFile('build/test.war').unzipTo(expandDir)
-        expandDir.assertHasDescendants(
-                'META-INF/MANIFEST.MF',
-                'WEB-INF/web.xml',
-                'WEB-INF/webinf1/file1.txt')
-    }
-
-    @Test public void canAddFilesToWebInfDir() {
-        createDir('web-inf') {
-            webinf1 {
-                file 'file1.txt'
-                file 'ignore.xml'
-            }
-        }
-        createDir('web-inf2') {
-            file 'file2.txt'
-        }
-
-        testFile('build.gradle') << '''
-            task war(type: War) {
-                webInf {
-                    from 'web-inf'
-                    exclude '**/*.xml'
-                }
-                webInf {
-                    from 'web-inf2'
-                    into 'dir2'
-                    include '**/file2*'
-                }
-                destinationDir = buildDir
-                archiveName = 'test.war'
-            }
-'''
-
-        inTestDirectory().withTasks('war').run()
-
-        TestFile expandDir = testFile('expanded')
-        testFile('build/test.war').unzipTo(expandDir)
-        expandDir.assertHasDescendants(
-                'META-INF/MANIFEST.MF',
-                'WEB-INF/webinf1/file1.txt',
-                'WEB-INF/dir2/file2.txt')
-    }
-
-    @Test public void canCreateArchivesAndExplodedImageFromSameSpec() {
-        createDir('test') {
-            dir1 {
-                file 'file1.txt'
-                file 'ignored.xml'
-            }
-            dir2 {
-                dir3 { file 'file2.txt' }
-                file 'ignored.xml'
-            }
-        }
-
-        testFile('build.gradle') << '''
-            def distImage = copySpec {
-                include '**/*.txt'
-                from('test/dir1') {
-                    into 'lib'
-                }
-                from('test/dir2') {
-                    into 'src'
-                }
-            }
-            task copy(type: Copy) {
-                into 'build/exploded'
-                with distImage
-            }
-            task zip(type: Zip) {
-                destinationDir = file('build')
-                archiveName = 'test.zip'
-                into 'prefix'
-                with distImage
-            }
-'''
-
-        inTestDirectory().withTasks('copy', 'zip').run()
-        testFile('build/exploded').assertHasDescendants(
-                'lib/file1.txt', 'src/dir3/file2.txt'
-        )
-        TestFile expandDir = testFile('expanded')
-        testFile('build/test.zip').unzipTo(expandDir)
-        expandDir.assertHasDescendants('prefix/lib/file1.txt', 'prefix/src/dir3/file2.txt')
-    }
-
-    @Test public void canCreateExplodedImageFromArchiveTask() {
-        createDir('test') {
-            dir1 {
-                file 'file1.txt'
-                file 'ignored.xml'
-            }
-            dir2 {
-                dir3 { file 'file2.txt' }
-                file 'ignored.xml'
-            }
-        }
-
-        testFile('build.gradle') << '''
-            task zip(type: Zip) {
-                destinationDir = file('build')
-                archiveName = 'test.zip'
-                into 'prefix'
-                from 'test'
-                include '**/*.txt'
-            }
-            task explodedZip(type: Copy) {
-                into 'build/exploded'
-                with zip
-            }
-            task copyFromRootSpec(type: Copy) {
-                into 'build/copy'
-                with zip.rootSpec
-            }
-'''
-
-        inTestDirectory().withTasks('explodedZip', 'copyFromRootSpec').run()
-        testFile('build/exploded').assertHasDescendants(
-                'prefix/dir1/file1.txt', 'prefix/dir2/dir3/file2.txt'
-        )
-        testFile('build/copy').assertHasDescendants(
-                'prefix/dir1/file1.txt', 'prefix/dir2/dir3/file2.txt'
-        )
-    }
-
-    @Test public void canMergeArchivesIntoAnotherZip() {
-        createZip('test.zip') {
-            shared {
-                file 'zip.txt'
-            }
-            zipdir1 {
-                file 'file1.txt'
-            }
-        }
-        createTar('test.tar') {
-            shared {
-                file 'tar.txt'
-            }
-            tardir1 {
-                file 'file1.txt'
-            }
-        }
-        createDir('test') {
-            shared {
-                file 'dir.txt'
-            }
-            dir1 {
-                file 'file1.txt'
-            }
-        }
-
-        testFile('build.gradle') << '''
-        task zip(type: Zip) {
-            from zipTree('test.zip')
-            from tarTree('test.tar')
-            from fileTree('test')
-            destinationDir = buildDir
-            archiveName = 'test.zip'
-        }
-        '''
-
-        inTestDirectory().withTasks('zip').run()
-
-        TestFile expandDir = testFile('expanded')
-        testFile('build/test.zip').unzipTo(expandDir)
-        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)
-        zipRoot.create(cl)
-        zipRoot.zipTo(zip)
-    }
-
-    private def createTar(String name, Closure cl) {
-        TestFile tarRoot = testFile("${name}.root")
-        TestFile tar = testFile(name)
-        tarRoot.create(cl)
-        tarRoot.tarTo(tar)
-    }
-
-    private def createDir(String name, Closure cl) {
-        TestFile root = testFile(name)
-        root.create(cl)
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/ArtifactDependenciesIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/ArtifactDependenciesIntegrationTest.groovy
deleted file mode 100644
index c59bd17..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/ArtifactDependenciesIntegrationTest.groovy
+++ /dev/null
@@ -1,168 +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.integtests.fixtures.ExecutionFailure
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.util.TestFile
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import static org.hamcrest.Matchers.*
-
-class ArtifactDependenciesIntegrationTest extends AbstractIntegrationTest {
-    @Rule
-    public final TestResources testResources = new TestResources()
-
-    @Before
-    public void setup() {
-        distribution.requireOwnUserHomeDir()
-    }
-    
-    @Test
-    public void canHaveConfigurationHierarchy() {
-        File buildFile = testFile("projectWithConfigurationHierarchy.gradle");
-        usingBuildFile(buildFile).run();
-    }
-
-    @Test
-    public void dependencyReportWithConflicts() {
-        File buildFile = testFile("projectWithConflicts.gradle");
-        usingBuildFile(buildFile).run();
-        usingBuildFile(buildFile).withDependencyList().run();
-    }
-
-    @Test
-    public void canNestModules() throws IOException {
-        File buildFile = testFile("projectWithNestedModules.gradle");
-        usingBuildFile(buildFile).run();
-    }
-
-    @Test
-    public void canHaveCycleInDependencyGraph() throws IOException {
-        File buildFile = testFile("projectWithCyclesInDependencyGraph.gradle");
-        usingBuildFile(buildFile).run();
-    }
-
-    @Test
-    public void canUseDynamicVersions() throws IOException {
-        File buildFile = testFile("projectWithDynamicVersions.gradle");
-        usingBuildFile(buildFile).run();
-    }
-
-    @Test
-    public void reportsUnknownDependencyError() {
-        File buildFile = testFile("projectWithUnknownDependency.gradle");
-        ExecutionFailure failure = usingBuildFile(buildFile).runWithFailure();
-        failure.assertHasFileName("Build file '" + buildFile.getPath() + "'");
-        failure.assertHasDescription("Execution failed for task ':listJars'");
-        failure.assertThatCause(startsWith("Could not resolve all dependencies for configuration ':compile'"));
-        failure.assertThatCause(containsString("unresolved dependency: test#unknownProjectA;1.2: not found"));
-        failure.assertThatCause(containsString("unresolved dependency: test#unknownProjectB;2.1.5: not found"));
-    }
-
-    @Test
-    public void reportsProjectDependsOnSelfError() {
-        TestFile buildFile = testFile("build.gradle");
-        buildFile << '''
-            configurations { compile }
-            dependencies { compile project(':') }
-            defaultTasks 'listJars'
-            task listJars << { configurations.compile.each { println it } }
-'''
-        ExecutionFailure failure = usingBuildFile(buildFile).runWithFailure();
-        failure.assertHasFileName("Build file '" + buildFile.getPath() + "'");
-        failure.assertHasDescription("Execution failed for task ':listJars'");
-        failure.assertThatCause(startsWith("Could not resolve all dependencies for configuration ':compile'"));
-        failure.assertThatCause(containsString("a module is not authorized to depend on itself"));
-    }
-
-    @Test
-    public void canSpecifyProducerTasksForFileDependency() {
-        testFile("settings.gradle").write("include 'sub'");
-        testFile("build.gradle") << '''
-            configurations { compile }
-            dependencies { compile project(path: ':sub', configuration: 'compile') }
-            task test(dependsOn: configurations.compile) << {
-                assert file('sub/sub.jar').isFile()
-            }
-'''
-        testFile("sub/build.gradle") << '''
-            configurations { compile }
-            dependencies { compile files('sub.jar') { builtBy 'jar' } }
-            task jar << { file('sub.jar').text = 'content' }
-'''
-
-        inTestDirectory().withTasks("test").run().assertTasksExecuted(":sub:jar", ":test");
-    }
-
-    @Test
-    public void resolvedProjectArtifactsContainProjectVersionInTheirNames() {
-        testFile('settings.gradle').write("include 'a', 'b'");
-        testFile('a/build.gradle') << '''
-            apply plugin: 'base'
-            configurations { compile }
-            task aJar(type: Jar) { }
-            artifacts { compile aJar }
-'''
-        testFile('b/build.gradle') << '''
-            apply plugin: 'base'
-            version = 'early'
-            configurations { compile }
-            task bJar(type: Jar) { }
-            gradle.taskGraph.whenReady { project.version = 'late' }
-            artifacts { compile bJar }
-'''
-        testFile('build.gradle') << '''
-            configurations { compile }
-            dependencies { compile project(path: ':a', configuration: 'compile'), project(path: ':b', configuration: 'compile') }
-            task test(dependsOn: configurations.compile) << {
-                assert configurations.compile.collect { it.name } == ['a.jar', 'b-late.jar']
-            }
-'''
-        inTestDirectory().withTasks('test').run()
-    }
-
-    @Test
-    public void canUseArtifactSelectorForProjectDependencies() {
-        testFile('settings.gradle').write("include 'a', 'b'");
-        testFile('a/build.gradle') << '''
-            apply plugin: 'base'
-            configurations { 'default' {} }
-            task aJar(type: Jar) { }
-            artifacts { 'default' aJar }
-'''
-        testFile('b/build.gradle') << '''
-            configurations { compile }
-            dependencies { compile(project(':a')) { artifact { name = 'a'; type = 'jar' } } }
-            task test {
-                inputs.files configurations.compile
-                doFirst {
-                    assert [project(':a').tasks.aJar.archivePath] as Set == configurations.compile.files
-                }
-            }
-'''
-        inTestDirectory().withTasks('test').run()
-    }
-
-    @Test
-    public void canHaveCycleInProjectDependencies() {
-        inTestDirectory().withTasks('listJars').run();
-    }
-}
-
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/BuildAggregationIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/BuildAggregationIntegrationTest.groovy
deleted file mode 100644
index 9049ad7..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/BuildAggregationIntegrationTest.groovy
+++ /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.integtests
-
-import org.gradle.integtests.fixtures.ExecutionFailure
-import org.gradle.integtests.fixtures.GradleDistribution
-import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.gradle.util.TestFile
-import org.junit.Rule
-import org.junit.Test
-import static org.hamcrest.Matchers.*
-
-class BuildAggregationIntegrationTest {
-    @Rule public final GradleDistribution dist = new GradleDistribution()
-    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
-
-    @Test
-    public void canExecuteAnotherBuildFromBuild() {
-        dist.testFile('build.gradle') << '''
-            assert gradle.parent == null
-            task build(type: GradleBuild) {
-                dir = 'other'
-                tasks = ['dostuff']
-                startParameter.searchUpwards = false
-            }
-'''
-
-        dist.testFile('other/build.gradle') << '''
-            assert gradle.parent != null
-            task dostuff << {
-                assert gradle.parent != null
-            }
-'''
-
-        executer.withTasks('build').run()
-    }
-
-    @Test
-    public void treatsBuildSrcProjectAsANestedBuild() {
-        dist.testFile('build.gradle') << '''
-            assert gradle.parent == null
-            task build
-'''
-
-        dist.testFile('buildSrc/build.gradle') << '''
-            apply plugin: 'java'
-            assert gradle.parent != null
-            classes << {
-                assert gradle.parent != null
-            }
-'''
-
-        executer.withTasks('build').run()
-    }
-
-    @Test
-    public void reportsNestedBuildFailure() {
-        TestFile other = dist.testFile('other.gradle') << '''
-            1/0
-'''
-
-        dist.testFile('build.gradle') << '''
-            task build(type: GradleBuild) {
-                buildFile = 'other.gradle'
-                startParameter.searchUpwards = false
-            }
-'''
-
-        ExecutionFailure failure = executer.withTasks('build').runWithFailure()
-        failure.assertHasFileName("Build file '${other}'")
-        failure.assertHasLineNumber(2)
-        failure.assertHasDescription('A problem occurred evaluating root project')
-        failure.assertThatCause(containsString('Division by zero'))
-    }
-
-    @Test
-    public void reportsBuildSrcFailure() {
-        dist.testFile('buildSrc/src/main/java/Broken.java') << 'broken!'
-        ExecutionFailure failure = executer.runWithFailure()
-        failure.assertHasFileName('Default buildSrc build script')
-        failure.assertHasDescription('Execution failed for task \':compileJava\'')
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/BuildScriptClasspathIntegrationTest.java b/subprojects/core/src/integTest/groovy/org/gradle/integtests/BuildScriptClasspathIntegrationTest.java
deleted file mode 100644
index c85fcca..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/BuildScriptClasspathIntegrationTest.java
+++ /dev/null
@@ -1,184 +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.integtests.fixtures.ArtifactBuilder;
-import org.gradle.integtests.fixtures.ExecutionFailure;
-import org.junit.Ignore;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-public class BuildScriptClasspathIntegrationTest extends AbstractIntegrationTest {
-    @Test
-    public void providesADefaultBuildForBuildSrcProject() {
-        testFile("buildSrc/src/main/java/BuildClass.java").writelns("public class BuildClass { }");
-        testFile("build.gradle").writelns("new BuildClass()");
-        inTestDirectory().withTaskList().run();
-    }
-
-    @Test
-    public void buildSrcProjectCanReferToSourceOutsideBuildSrcDir() {
-        testFile("gradle/src/BuildClass.java").writelns("public class BuildClass { }");
-        testFile("buildSrc/build.gradle").writelns(
-                "apply plugin: 'java'",
-                "sourceSets.main.java.srcDirs = ['../gradle/src']"
-        );
-        testFile("build.gradle").writelns(
-                "task test << { new BuildClass() }"
-        );
-
-        inTestDirectory().withTasks("test").run();
-
-        testFile("gradle/src/BuildClass.java").writelns("public class BuildClass { public BuildClass(String value) { throw new RuntimeException(\"broken\"); } }");
-
-        ExecutionFailure failure = inTestDirectory().withTasks("test").runWithFailure();
-        failure.assertHasCause("broken");
-    }
-
-    @Test
-    public void canDeclareClasspathInBuildScript() {
-        ArtifactBuilder builder = artifactBuilder();
-        builder.sourceFile("org/gradle/test/ImportedClass.java").writelns(
-                "package org.gradle.test;",
-                "public class ImportedClass { }"
-        );
-        builder.sourceFile("org/gradle/test/StaticImportedClass.java").writelns(
-                "package org.gradle.test;",
-                "public class StaticImportedClass { public static int someValue = 12; }"
-        );
-        builder.sourceFile("org/gradle/test/StaticImportedFieldClass.java").writelns(
-                "package org.gradle.test;",
-                "public class StaticImportedFieldClass { public static int anotherValue = 4; }"
-        );
-        builder.sourceFile("org/gradle/test2/OnDemandImportedClass.java").writelns(
-                "package org.gradle.test2;",
-                "public class OnDemandImportedClass { }"
-        );
-        builder.buildJar(testFile("repo/test-1.3.jar"));
-
-        testFile("build.gradle").writelns(
-                "import org.gradle.test.ImportedClass",
-                "import static org.gradle.test.StaticImportedClass.*",
-                "import static org.gradle.test.StaticImportedFieldClass.anotherValue",
-                "import org.gradle.test2.*",
-                "buildscript {",
-                "  repositories {",
-                "    flatDir dirs: file('repo')",
-                "  }",
-                "  dependencies {",
-                "    classpath name: 'test', version: '1.+'",
-                "  }",
-                "}",
-                "task hello << {",
-                "  new org.gradle.test.ImportedClass()",
-                "  println someValue",
-                "  println anotherValue",
-                "  new ImportedClass()",
-                "  new OnDemandImportedClass()",
-                "}",
-                "a = new ImportedClass()",
-                "b = OnDemandImportedClass",
-                "c = someValue",
-                "d = anotherValue",
-                "class TestClass extends ImportedClass { }",
-                "def aMethod() { return new OnDemandImportedClass() }"
-        );
-        inTestDirectory().withTasks("hello").run();
-    }
-
-    @Test
-    public void canUseBuildSrcAndSystemClassesInClasspathDeclaration() {
-        testFile("buildSrc/src/main/java/org/gradle/buildsrc/test/ImportedClass.java").writelns(
-                "package org.gradle.buildsrc.test;",
-                "public class ImportedClass { }"
-        );
-        testFile("buildSrc/src/main/java/org/gradle/buildsrc/test/StaticImportedClass.java").writelns(
-                "package org.gradle.buildsrc.test;",
-                "public class StaticImportedClass { public static int someValue = 12; }"
-        );
-        testFile("buildSrc/src/main/java/org/gradle/buildsrc/test/StaticImportedFieldClass.java").writelns(
-                "package org.gradle.buildsrc.test;",
-                "public class StaticImportedFieldClass { public static int anotherValue = 4; }"
-        );
-        testFile("buildSrc/src/main/java/org/gradle/buildsrc/test2/OnDemandImportedClass.java").writelns(
-                "package org.gradle.buildsrc.test2;",
-                "public class OnDemandImportedClass { }"
-        );
-
-        testFile("build.gradle").writelns(
-                "import org.gradle.buildsrc.test.ImportedClass",
-                "import org.gradle.buildsrc.test2.*",
-                "import static org.gradle.buildsrc.test.StaticImportedClass.*",
-                "import static org.gradle.buildsrc.test.StaticImportedFieldClass.anotherValue",
-                "buildscript {",
-                "    new ImportedClass()",
-                "    new org.gradle.buildsrc.test.ImportedClass()",
-                "    new org.gradle.buildsrc.test2.OnDemandImportedClass()",
-                "    println someValue",
-                "    println anotherValue",
-                "    List l = new ArrayList()",
-                "    Project p = project",
-                "    Closure cl = { }",
-                "}",
-                "task hello"
-        );
-        inTestDirectory().withTasks("hello").run();
-    }
-
-    @Test
-    public void inheritsClassPathOfParentProject() {
-        ArtifactBuilder builder = artifactBuilder();
-        builder.sourceFile("org/gradle/test/BuildClass.java").writelns(
-                "package org.gradle.test;",
-                "public class BuildClass { }"
-        );
-        builder.buildJar(testFile("repo/test-1.3.jar"));
-        testFile("settings.gradle").writelns(
-                "include 'child'"
-        );
-        testFile("build.gradle").writelns(
-                "assert gradle.scriptClassLoader == buildscript.classLoader.parent",
-                "buildscript {",
-                "    repositories { flatDir(dirs: file('repo')) }",
-                "    dependencies { classpath name: 'test', version: '1.3' }",
-                "}"
-        );
-        testFile("child/build.gradle").writelns(
-                "assert parent.buildscript.classLoader == buildscript.classLoader.parent",
-                "task hello << ",
-                "{",
-                "    new org.gradle.test.BuildClass()",
-                "}"
-        );
-        inTestDirectory().withTasks("hello").run();
-    }
-
-    @Test @Ignore
-    public void reportsFailureDuringClasspathDeclaration() {
-        fail("implement me");
-    }
-
-    @Test @Ignore
-    public void canInjectClassPathIntoSubProjects() {
-        fail("implement me");
-    }
-    
-    @Test @Ignore
-    public void canReuseClassPathRepositories() {
-        fail("implement me");
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/BuildScriptErrorIntegrationTest.java b/subprojects/core/src/integTest/groovy/org/gradle/integtests/BuildScriptErrorIntegrationTest.java
deleted file mode 100644
index adb5f9a..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/BuildScriptErrorIntegrationTest.java
+++ /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.integtests.fixtures.ExecutionFailure;
-import org.gradle.util.TestFile;
-import org.junit.Ignore;
-import org.junit.Test;
-
-import java.io.File;
-
-public class BuildScriptErrorIntegrationTest extends AbstractIntegrationTest {
-    @Test
-    public void reportsProjectEvaluationFailsWithGroovyException() {
-        TestFile buildFile = testFile("build.gradle");
-        buildFile.writelns("", "createTakk('do-stuff')");
-        ExecutionFailure failure = usingBuildFile(buildFile).runWithFailure();
-
-        failure.assertHasFileName(String.format("Build file '%s'", buildFile));
-        failure.assertHasLineNumber(2);
-        failure.assertHasDescription("A problem occurred evaluating root project 'reportsProjectEvaluationFailsWithGroovyException");
-        failure.assertHasCause("Could not find method createTakk() for arguments [do-stuff] on root project 'reportsProjectEvaluationFailsWithGroovyException");
-    }
-
-    @Test
-    public void reportsScriptCompilationException() {
-        TestFile buildFile = testFile("build.gradle");
-        buildFile.writelns(
-            "// a comment",
-            "import org.gradle.unknown.Unknown",
-            "new Unknown()");
-        ExecutionFailure failure = inTestDirectory().runWithFailure();
-        failure.assertHasFileName(String.format("Build file '%s'", buildFile));
-        failure.assertHasLineNumber(2);
-        failure.assertHasDescription(String.format("Could not compile build file '%s'.", buildFile));
-    }
-
-    @Test
-    public void reportsNestedProjectEvaluationFailsWithRuntimeException() {
-        testFile("settings.gradle").write("include 'child'");
-
-        TestFile buildFile = testFile("build.gradle");
-        buildFile.writelns(
-                "dependsOn 'child'",
-                "task t");
-
-        TestFile childBuildFile = testFile("child/build.gradle");
-        childBuildFile.writelns(
-                "def broken = { ->",
-                "    throw new RuntimeException('failure') }",
-                "broken()");
-        ExecutionFailure failure = inTestDirectory().withTasks("t").runWithFailure();
-
-        failure.assertHasFileName(String.format("Build file '%s'", childBuildFile));
-        failure.assertHasLineNumber(2);
-        failure.assertHasDescription("A problem occurred evaluating project ':child'");
-        failure.assertHasCause("failure");
-    }
-
-    @Test
-    public void reportsTaskActionExecutionFailsWithError() {
-        TestFile buildFile = testFile("build.gradle");
-        buildFile.writelns(
-                "task('do-stuff').doFirst",
-                "{",
-                "1/0",
-                "}");
-        ExecutionFailure failure = usingBuildFile(buildFile).withTasks("do-stuff").runWithFailure();
-
-        failure.assertHasFileName(String.format("Build file '%s'", buildFile));
-        failure.assertHasLineNumber(3);
-        failure.assertHasDescription("Execution failed for task ':do-stuff'");
-        failure.assertHasCause("Division by zero");
-    }
-
-    @Test
-    public void reportsTaskActionExecutionFailsWithRuntimeException() {
-        File buildFile = testFile("build.gradle").writelns(
-                "task brokenClosure << {",
-                "    throw new RuntimeException('broken closure')",
-                "}");
-
-        ExecutionFailure failure = usingBuildFile(buildFile).withTasks("brokenClosure").runWithFailure();
-
-        failure.assertHasFileName(String.format("Build file '%s'", buildFile));
-        failure.assertHasLineNumber(2);
-        failure.assertHasDescription("Execution failed for task ':brokenClosure'");
-        failure.assertHasCause("broken closure");
-    }
-
-    @Test
-    public void reportsTaskActionExecutionFailsFromJavaWithRuntimeException() {
-        testFile("buildSrc/src/main/java/org/gradle/BrokenTask.java").writelns(
-                "package org.gradle;",
-                "import org.gradle.api.Action;",
-                "import org.gradle.api.DefaultTask;",
-                "import org.gradle.api.Task;",
-                "public class BrokenTask extends DefaultTask {",
-                "    public BrokenTask() {",
-                "        doFirst(new Action<Task>() {",
-                "            public void execute(Task task) {",
-                "                throw new RuntimeException(\"broken action\");",
-                "            }",
-                "        });",
-                "    }",
-                "}"
-        );
-        File buildFile = testFile("build.gradle").write("task brokenJavaTask(type: org.gradle.BrokenTask)");
-
-        ExecutionFailure failure = usingBuildFile(buildFile).withTasks("brokenJavaTask").runWithFailure();
-
-        failure.assertHasFileName(String.format("Build file '%s'", buildFile));
-        failure.assertHasDescription("Execution failed for task ':brokenJavaTask'");
-        failure.assertHasCause("broken action");
-    }
-
-    @Test
-    public void reportsTaskInjectedByOtherProjectFailsWithRuntimeException() {
-        testFile("settings.gradle").write("include 'a', 'b'");
-        TestFile buildFile = testFile("b/build.gradle");
-        buildFile.writelns(
-                "project(':a') {",
-                "    task a << {",
-                "        throw new RuntimeException('broken')",
-                "    }",
-                "}");
-
-        ExecutionFailure failure = inTestDirectory().withTasks("a").runWithFailure();
-
-        failure.assertHasFileName(String.format("Build file '%s'", buildFile));
-        failure.assertHasLineNumber(3);
-        failure.assertHasDescription("Execution failed for task ':a:a");
-        failure.assertHasCause("broken");
-    }
-
-    @Test
-    public void reportsTaskGraphReadyEventFailsWithRuntimeException() {
-        TestFile buildFile = testFile("build.gradle");
-        buildFile.writelns(
-                "gradle.taskGraph.whenReady {",
-                "throw new RuntimeException('broken closure')",
-                "}",
-                "task a");
-
-        ExecutionFailure failure = usingBuildFile(buildFile).withTasks("a").runWithFailure();
-
-        failure.assertHasFileName(String.format("Build file '%s'", buildFile));
-        failure.assertHasLineNumber(2);
-        failure.assertHasDescription("Failed to notify task execution graph listener");
-        failure.assertHasCause("broken closure");
-    }
-
-    @Test @Ignore
-    public void reportsTaskDependencyClosureFailsWithRuntimeException() {
-        TestFile buildFile = testFile("build.gradle");
-        buildFile.writelns(
-                "task a",
-                "a.dependsOn {",
-                "throw new RuntimeException('broken')",
-                "}");
-
-        ExecutionFailure failure = usingBuildFile(buildFile).withTasks("a").runWithFailure();
-
-        failure.assertHasFileName(String.format("Build file '%s'", buildFile));
-        failure.assertHasLineNumber(3);
-        failure.assertHasDescription("??");
-        failure.assertHasCause("broken");
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/BuildScriptExecutionIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/BuildScriptExecutionIntegrationTest.groovy
deleted file mode 100644
index 257066f..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/BuildScriptExecutionIntegrationTest.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.integtests
-
-import org.gradle.integtests.fixtures.ExecutionResult
-import org.gradle.util.TestFile
-import org.junit.Test
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
-
-class BuildScriptExecutionIntegrationTest extends AbstractIntegrationTest {
-
-    @Test
-    public void executesBuildScriptWithCorrectEnvironment() {
-        TestFile buildScript = testFile('build.gradle')
-        buildScript << """
-            println 'quiet message'
-            captureStandardOutput(LogLevel.ERROR)
-            println 'error message'
-            assert project != null
-            assert "${buildScript.absolutePath.replace("\\", "\\\\")}" == buildscript.sourceFile as String
-            assert "${buildScript.toURI()}" == buildscript.sourceURI as String
-            assert buildscript.classLoader == getClass().classLoader.parent
-            assert buildscript.classLoader == Thread.currentThread().contextClassLoader
-            assert gradle.scriptClassLoader == buildscript.classLoader.parent
-
-            task doStuff
-"""
-
-        ExecutionResult result = inTestDirectory().withTasks('doStuff').run()
-        assertThat(result.output, containsString('quiet message'))
-        assertThat(result.output, not(containsString('error message')))
-        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/core/src/integTest/groovy/org/gradle/integtests/CacheProjectIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/CacheProjectIntegrationTest.groovy
deleted file mode 100644
index a7e13a4..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/CacheProjectIntegrationTest.groovy
+++ /dev/null
@@ -1,123 +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.groovy.scripts.ScriptSource
-import org.gradle.groovy.scripts.UriScriptSource
-import org.gradle.integtests.fixtures.GradleDistribution
-import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.gradle.util.GradleVersion
-import org.gradle.util.TestFile
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import static org.junit.Assert.*
-
-/**
- * @author Hans Dockter
- */
-class CacheProjectIntegrationTest {
-    static final String TEST_FILE = "build/test.txt"
-
-    @Rule public final GradleDistribution dist = new GradleDistribution()
-    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
-
-    TestFile projectDir
-    TestFile userHomeDir
-    TestFile buildFile
-    TestFile propertiesFile
-    TestFile classFile
-    TestFile artifactsCache
-
-    @Before
-    public void setUp() {
-        String version = new GradleVersion().version
-        projectDir = dist.getTestDir().file("project")
-        projectDir.mkdirs()
-        userHomeDir = dist.getUserHomeDir()
-        buildFile = projectDir.file('build.gradle')
-        ScriptSource source = new UriScriptSource("build file", buildFile)
-        propertiesFile = userHomeDir.file("caches/$version/scripts/$source.className/cache.properties")
-        classFile = userHomeDir.file("caches/$version/scripts/$source.className/no_buildscript_ProjectScript/${source.className}.class")
-        artifactsCache = projectDir.file(".gradle/$version/taskArtifacts/cache.bin")
-    }
-
-    @Test
-    public void cachesBuildScript() {
-        createLargeBuildScript()
-        testBuild("hello1", "Hello 1")
-        TestFile.Snapshot classFileSnapshot = classFile.snapshot()
-        TestFile.Snapshot artifactsCacheSnapshot = artifactsCache.snapshot()
-
-        testBuild("hello2", "Hello 2")
-        classFile.assertHasNotChangedSince(classFileSnapshot)
-        artifactsCache.assertHasNotChangedSince(artifactsCacheSnapshot)
-
-        modifyLargeBuildScript()
-        testBuild("newTask", "I am new")
-        classFile.assertHasChangedSince(classFileSnapshot)
-        artifactsCache.assertHasNotChangedSince(artifactsCacheSnapshot)
-        classFileSnapshot = classFile.snapshot()
-        artifactsCacheSnapshot = artifactsCache.snapshot()
-
-        testBuild("newTask", "I am new", "-Crebuild")
-        classFile.assertHasChangedSince(classFileSnapshot)
-        artifactsCache.assertHasChangedSince(artifactsCacheSnapshot)
-    }
-
-    private def testBuild(String taskName, String expected, String... args) {
-        executer.inDirectory(projectDir).withTasks(taskName).withArguments(args).withQuietLogging().run()
-        assertEquals(expected, projectDir.file(TEST_FILE).text)
-        classFile.assertIsFile()
-        propertiesFile.assertIsFile()
-        artifactsCache.assertIsFile()
-    }
-
-    // We once ran into a cache problem under windows, which was not reproducible with small build scripts. Therefore we
-    // create a larger one here.
-
-    def createLargeBuildScript() {
-        File buildFile = projectDir.file('build.gradle')
-        String content = ""
-        50.times {i ->
-            content += """task 'hello$i' << {
-    File file = file('$TEST_FILE')
-    file.parentFile.mkdirs()
-    file.write('Hello $i')
-}
-
-void someMethod$i() {
-    println('Some message')
-}
-
-"""
-        }
-        buildFile.write(content)
-    }
-
-    def void modifyLargeBuildScript() {
-        File buildFile = projectDir.file('build.gradle')
-        String newContent = buildFile.text + """
-task newTask << {
-    File file = file('$TEST_FILE')
-    file.parentFile.mkdirs()
-    file.write('I am new')
-}
-"""
-        buildFile.write(newContent)
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/CodeQualityIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/CodeQualityIntegrationTest.groovy
deleted file mode 100644
index 6b3d8a2..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/CodeQualityIntegrationTest.groovy
+++ /dev/null
@@ -1,187 +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.integtests.fixtures.ExecutionFailure
-import org.gradle.util.TestFile
-import org.hamcrest.Matcher
-import org.junit.Test
-import static org.gradle.util.Matchers.*
-import static org.hamcrest.Matchers.*
-
-class CodeQualityIntegrationTest extends AbstractIntegrationTest {
-    @Test
-    public void handlesEmptyProjects() {
-        testFile('build.gradle') << '''
-apply plugin: 'groovy'
-apply plugin: 'code-quality'
-'''
-        inTestDirectory().withTasks('check').run()
-    }
-
-    @Test
-    public void generatesReportForJavaSource() {
-        testFile('build.gradle') << '''
-apply plugin: 'java'
-apply plugin: 'code-quality'
-'''
-        writeCheckstyleConfig()
-
-        testFile('src/main/java/org/gradle/Class1.java') << 'package org.gradle; class Class1 { }'
-        testFile('src/test/java/org/gradle/TestClass1.java') << 'package org.gradle; class TestClass1 { }'
-
-        inTestDirectory().withTasks('check').run()
-
-        testFile('build/checkstyle/main.xml').assertContents(containsClass('org.gradle.Class1'))
-        testFile('build/checkstyle/test.xml').assertContents(containsClass('org.gradle.TestClass1'))
-    }
-
-    @Test
-    public void generatesReportForJavaSourceInGroovySourceDirs() {
-        testFile('build.gradle') << '''
-apply plugin: 'groovy'
-apply plugin: 'code-quality'
-dependencies { groovy localGroovy() }
-'''
-        writeCheckstyleConfig()
-
-        testFile('src/main/groovy/org/gradle/Class1.java') << 'package org.gradle; class Class1 { }'
-        testFile('src/test/groovy/org/gradle/TestClass1.java') << 'package org.gradle; class TestClass1 { }'
-
-        inTestDirectory().withTasks('check').run()
-
-        testFile('build/checkstyle/main.xml').assertContents(containsClass('org.gradle.Class1'))
-        testFile('build/checkstyle/test.xml').assertContents(containsClass('org.gradle.TestClass1'))
-    }
-
-    private Matcher<String> containsClass(String classname) {
-        return containsLine(containsString(classname.replace('.', File.separator) + '.java'))
-    }
-
-    @Test
-    public void checkstyleOnlyChecksJavaSource() {
-        testFile('build.gradle') << '''
-apply plugin: 'groovy'
-apply plugin: 'code-quality'
-'''
-        writeCheckstyleConfig()
-
-        testFile('src/main/groovy/org/gradle/Class1.java') << 'package org.gradle; class Class1 { }'
-        testFile('src/main/groovy/org/gradle/Class2.java') << 'package org.gradle; class Class2 { }'
-        testFile('src/main/groovy/org/gradle/class3.groovy') << 'package org.gradle; class class3 { }'
-
-        inTestDirectory().withTasks('checkstyleMain').run()
-
-        testFile('build/checkstyle/main.xml').assertExists()
-        testFile('build/checkstyle/main.xml').assertContents(not(containsClass('org.gradle.class3')))
-    }
-
-    @Test
-    public void checkstyleViolationBreaksBuild() {
-        testFile('build.gradle') << '''
-apply plugin: 'groovy'
-apply plugin: 'code-quality'
-'''
-        writeCheckstyleConfig()
-
-        testFile('src/main/java/org/gradle/class1.java') << 'package org.gradle; class class1 { }'
-        testFile('src/main/groovy/org/gradle/class2.java') << 'package org.gradle; class class2 { }'
-
-        ExecutionFailure failure = inTestDirectory().withTasks('check').runWithFailure()
-        failure.assertHasDescription('Execution failed for task \':checkstyleMain\'')
-        failure.assertThatCause(startsWith('Checkstyle check violations were found in main Java source. See the report at'))
-
-        testFile('build/checkstyle/main.xml').assertExists()
-    }
-
-    @Test
-    public void generatesReportForGroovySource() {
-        testFile('build.gradle') << '''
-apply plugin: 'groovy'
-apply plugin: 'code-quality'
-dependencies { groovy localGroovy() }
-'''
-        writeCodeNarcConfigFile()
-
-        testFile('src/main/groovy/org/gradle/Class1.groovy') << 'package org.gradle; class Class1 { }'
-        testFile('src/test/groovy/org/gradle/TestClass1.groovy') << 'package org.gradle; class TestClass1 { }'
-
-        inTestDirectory().withTasks('check').run()
-
-        testFile('build/reports/codenarc/main.html').assertExists()
-        testFile('build/reports/codenarc/test.html').assertExists()
-    }
-
-    @Test
-    public void codeNarcOnlyChecksGroovySource() {
-        testFile('build.gradle') << '''
-apply plugin: 'groovy'
-apply plugin: 'code-quality'
-'''
-
-        writeCodeNarcConfigFile()
-
-        testFile('src/main/groovy/org/gradle/class1.java') << 'package org.gradle; class class1 { }'
-        testFile('src/main/groovy/org/gradle/Class2.groovy') << 'package org.gradle; class Class2 { }'
-
-        inTestDirectory().withTasks('codenarcMain').run()
-
-        testFile('build/reports/codenarc/main.html').assertExists()
-    }
-
-    @Test
-    public void codeNarcViolationBreaksBuild() {
-        testFile('build.gradle') << '''
-apply plugin: 'groovy'
-apply plugin: 'code-quality'
-dependencies { groovy localGroovy() }
-'''
-
-        writeCodeNarcConfigFile()
-
-        testFile('src/main/groovy/org/gradle/class1.groovy') << 'package org.gradle; class class1 { }'
-
-        ExecutionFailure failure = inTestDirectory().withTasks('check').runWithFailure()
-        failure.assertHasDescription('Execution failed for task \':codenarcMain\'')
-        failure.assertThatCause(startsWith('CodeNarc check violations were found in main Groovy source. See the report at '))
-
-        testFile('build/reports/codenarc/main.html').assertExists()
-    }
-
-    private TestFile writeCheckstyleConfig() {
-        return testFile('config/checkstyle/checkstyle.xml') << '''
-<!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="TreeWalker">
-        <module name="TypeName"/>
-    </module>
-</module>'''
-    }
-
-    private TestFile writeCodeNarcConfigFile() {
-        return testFile('config/codenarc/codenarc.xml') << '''
-<ruleset xmlns="http://codenarc.org/ruleset/1.0"
-        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-        xsi:schemaLocation="http://codenarc.org/ruleset/1.0 http://codenarc.org/ruleset-schema.xsd"
-        xsi:noNamespaceSchemaLocation="http://codenarc.org/ruleset-schema.xsd">
-    <ruleset-ref path='rulesets/naming.xml'/>
-</ruleset>
-'''
-    }
-
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/CommandLineIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/CommandLineIntegrationTest.groovy
deleted file mode 100644
index ec72b50..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/CommandLineIntegrationTest.groovy
+++ /dev/null
@@ -1,130 +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.integtests.fixtures.ExecutionFailure
-import org.gradle.integtests.fixtures.GradleDistribution
-import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.util.Jvm
-import org.gradle.util.OperatingSystem
-import org.junit.Ignore
-import org.junit.Rule
-import org.junit.Test
-
-public class CommandLineIntegrationTest {
-    @Rule public final GradleDistribution dist = new GradleDistribution()
-    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
-    @Rule public final TestResources resources = new TestResources()
-
-    @Test
-    public void hasNonZeroExitCodeOnBuildFailure() {
-        ExecutionFailure failure = executer.withTasks('unknown').runWithFailure()
-        failure.assertHasDescription("Task 'unknown' not found in root project 'commandLine'.")
-    }
-
-    @Test
-    public void canonicalisesWorkingDirectory() {
-        File javaprojectDir;
-        if (OperatingSystem.current().isWindows()) {
-            javaprojectDir = new File(dist.samplesDir, 'java/QUICKS~1')
-        } else if (!OperatingSystem.current().isCaseSensitiveFileSystem()) {
-            javaprojectDir = new File(dist.samplesDir, 'JAVA/QuickStart')
-        } else {
-            javaprojectDir = new File(dist.samplesDir, 'java/multiproject/../quickstart')
-        }
-        executer.inDirectory(javaprojectDir).withTasks('classes').run()
-    }
-
-    @Test
-    public void canDefineJavaHomeViaEnvironmentVariable() {
-        String javaHome = Jvm.current().javaHome
-        String expectedJavaHome = "-PexpectedJavaHome=${javaHome}"
-
-        // Handle java on the system PATH, with no JAVA_HOME specified
-        String path = String.format('%s%s%s', Jvm.current().javaExecutable.parentFile, File.pathSeparator, System.getenv('PATH'))
-        executer.withEnvironmentVars('PATH': path, 'JAVA_HOME': '')
-                .withArguments(expectedJavaHome)
-                .withTasks('checkJavaHome')
-                .run()
-
-        // Handle JAVA_HOME specified
-        executer.withEnvironmentVars('JAVA_HOME': javaHome)
-                .withArguments(expectedJavaHome)
-                .withTasks('checkJavaHome')
-                .run()
-
-        // Handle JAVA_HOME with trailing separator
-        executer.withEnvironmentVars('JAVA_HOME': javaHome + File.separator)
-                .withArguments(expectedJavaHome)
-                .withTasks('checkJavaHome')
-                .run()
-
-        if (!OperatingSystem.current().isWindows()) {
-            return
-        }
-
-        // Handle JAVA_HOME wrapped in quotes
-        executer.withEnvironmentVars('JAVA_HOME': "\"$javaHome\"")
-                .withArguments(expectedJavaHome)
-                .withTasks('checkJavaHome')
-                .run()
-
-        // Handle JAVA_HOME with slash separators. This is allowed by the JVM
-        executer.withEnvironmentVars('JAVA_HOME': javaHome.replace(File.separator, '/'))
-                .withArguments(expectedJavaHome)
-                .withTasks('checkJavaHome')
-                .run()
-    }
-
-    @Test
-    public void failsWhenJavaHomeDoetNotPointToAJavaInstallation() {
-        def failure = executer.withEnvironmentVars('JAVA_HOME': dist.testDir)
-                .withTasks('checkJavaHome')
-                .runWithFailure()
-        assert failure.output.contains('ERROR: JAVA_HOME is set to an invalid directory')
-    }
-
-    @Test
-    public void canDefineGradleUserHomeViaEnvironmentVariable() {
-        // the actual testing is done in the build script.
-        File gradleUserHomeDir = dist.testDir.file('customUserHome')
-        executer.withUserHomeDir(null)
-                .withEnvironmentVars('GRADLE_USER_HOME': gradleUserHomeDir.absolutePath)
-                .withTasks("checkGradleUserHomeViaSystemEnv")
-                .run();
-    }
-
-    @Test
-    public void checkDefaultGradleUserHome() {
-        // the actual testing is done in the build script.
-        executer.withUserHomeDir(null).
-                withTasks("checkDefaultGradleUserHome")
-                .run();
-    }
-
-    @Test @Ignore
-    public void systemPropGradleUserHomeHasPrecedenceOverEnvVariable() {
-        // the actual testing is done in the build script.
-        File gradleUserHomeDir = dist.testFile("customUserHome")
-        File systemPropGradleUserHomeDir = dist.testFile("systemPropCustomUserHome")
-        executer.withUserHomeDir(null)
-                .withArguments("-Dgradle.user.home=" + systemPropGradleUserHomeDir.absolutePath)
-                .withEnvironmentVars('GRADLE_USER_HOME': gradleUserHomeDir.absolutePath)
-                .withTasks("checkSystemPropertyGradleUserHomeHasPrecedence")
-                .run()
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/CopyErrorIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/CopyErrorIntegrationTest.groovy
deleted file mode 100644
index bd292dc..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/CopyErrorIntegrationTest.groovy
+++ /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.integtests
-
-import org.gradle.integtests.fixtures.ExecutionFailure
-import org.gradle.util.OperatingSystem
-import org.gradle.util.TestFile
-import org.junit.Assert
-import org.junit.Test
-
-class CopyErrorIntegrationTest extends AbstractIntegrationTest {
-    @Test
-    public void reportsSymLinkWhichPointsToNothing() {
-        if (OperatingSystem.current().isWindows()) {
-            return
-        }
-
-        TestFile link = testFile('src/file')
-        link.linkTo(testFile('missing'))
-
-        Assert.assertFalse(link.isDirectory())
-        Assert.assertFalse(link.isFile())
-        Assert.assertFalse(link.exists())
-
-        testFile('build.gradle') << '''
-            task copy(type: Copy) {
-                from 'src'
-                into 'dest'
-            }
-'''
-
-        ExecutionFailure failure = inTestDirectory().withTasks('copy').runWithFailure()
-        failure.assertHasDescription("Could not list contents of '${link}'.")
-    }
-
-    @Test
-    public void reportsUnreadableSourceDir() {
-        if (OperatingSystem.current().isWindows()) {
-            return
-        }
-
-        TestFile dir = testFile('src').createDir()
-        dir.permissions = '-w-r--r--'
-
-        Assert.assertTrue(dir.isDirectory())
-        Assert.assertTrue(dir.exists())
-        Assert.assertFalse(dir.canRead())
-        Assert.assertTrue(dir.canWrite())
-
-        testFile('build.gradle') << '''
-            task copy(type: Copy) {
-                from 'src'
-                into 'dest'
-            }
-'''
-
-        ExecutionFailure failure = inTestDirectory().withTasks('copy').runWithFailure()
-        failure.assertHasDescription("Could not list contents of directory '${dir}' as it is not readable.")
-
-        dir.permissions = 'rwxr--r--'
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/CopyTaskIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/CopyTaskIntegrationTest.groovy
deleted file mode 100644
index 8692d21..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/CopyTaskIntegrationTest.groovy
+++ /dev/null
@@ -1,320 +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.integtests.fixtures.TestResources
-import org.gradle.util.TestFile
-import org.junit.Rule
-import org.junit.Test
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
-
-public class CopyTaskIntegrationTest extends AbstractIntegrationTest {
-    @Rule
-    public final TestResources resources = new TestResources("copyTestResources")
-
-    @Test
-    public void testSingleSourceWithIncludeAndExclude() {
-        TestFile buildFile = testFile("build.gradle") << '''
-            task (copy, type:Copy) {
-               from 'src'
-               into 'dest'
-               include '**/sub/**'
-               exclude '**/ignore/**'
-            }
-'''
-        usingBuildFile(buildFile).withTasks("copy").run()
-        testFile('dest').assertHasDescendants(
-                'one/sub/onesub.a',
-                'one/sub/onesub.b'
-        )
-    }
-
-   @Test
-   public void testSingleSourceWithSpecClosures() {
-       TestFile buildFile = testFile("build.gradle").writelns(
-               "task (copy, type:Copy) {",
-               "   from 'src'",
-               "   into 'dest'",
-               "   include { fte -> !fte.file.name.endsWith('b') }",
-               "   exclude { fte -> fte.file.name == 'bad.file' }",
-               "}"
-       )
-       usingBuildFile(buildFile).withTasks("copy").run()
-       testFile('dest').assertHasDescendants(
-               'root.a',
-               'one/one.a',
-               'two/two.a',
-       )
-   }
-
-    @Test
-    public void testMultipleSourceWithInheritedPatterns() {
-        TestFile buildFile = testFile("build.gradle") << '''
-            task (copy, type:Copy) {
-               into 'dest'
-               from('src/one') {
-                  into '1'
-                  include '**/*.a'
-               }
-               from('src/two') {
-                  into '2'
-                  include '**/*.b'
-               }
-               exclude '**/ignore/**'
-            }
-'''
-        usingBuildFile(buildFile).withTasks("copy").run()
-        testFile('dest').assertHasDescendants(
-                '1/one.a',
-                '1/sub/onesub.a',
-                '2/two.b',
-        )
-    }
-
-    @Test
-    public void testMultipleSourcesWithInheritedDestination() {
-        TestFile buildFile = testFile("build.gradle") << '''
-            task (copy, type:Copy) {
-               into 'dest'
-               into('common') {
-                  from('src/one') {
-                     into 'a/one'
-                     include '*.a'
-                  }
-                  into('b') {
-                     from('src/two') {
-                        into 'two'
-                        include '**/*.b'
-                     }
-                  }
-               }
-            }
-'''
-        usingBuildFile(buildFile).withTasks("copy").run()
-        testFile('dest').assertHasDescendants(
-                'common/a/one/one.a',
-                'common/b/two/two.b',
-        )
-    }
-
-    @Test void testRename() {
-        TestFile buildFile = testFile("build.gradle") << '''
-            task (copy, type:Copy) {
-               from 'src'
-               into 'dest'
-               exclude '**/ignore/**'
-               rename '(.*).a', '\$1.renamed'
-               rename { it.startsWith('one.') ? "renamed_$it" : it }
-            }
-'''
-        usingBuildFile(buildFile).withTasks("copy").run()
-        testFile('dest').assertHasDescendants(
-                'root.renamed',
-                'root.b',
-                'one/renamed_one.renamed',
-                'one/renamed_one.b',
-                'one/sub/onesub.renamed',
-                'one/sub/onesub.b',
-                'two/two.renamed',
-                'two/two.b'
-        )
-    }
-
-    @Test
-    public void testCopyAction() {
-        TestFile buildFile = testFile("build.gradle").writelns(
-                "task copyIt << {",
-                "   copy {",
-                "      from 'src'",
-                "      into 'dest'",
-                "      exclude '**/ignore/**'",
-                "   }",
-                "}"
-        )
-        usingBuildFile(buildFile).withTasks("copyIt").run()
-        testFile('dest').assertHasDescendants(
-                'root.a',
-                'root.b',
-                'one/one.a',
-                'one/one.b',
-                'one/sub/onesub.a',
-                'one/sub/onesub.b',
-                'two/two.a',
-                'two/two.b',
-        )
-    }
-
-    @Test public void copySingleFiles() {
-        TestFile buildFile = testFile("build.gradle").writelns(
-                "task copyIt << {",
-                "   copy {",
-                "      from 'src/one/one.a', 'src/two/two.a'",
-                "      into 'dest/two'",
-                "   }",
-                "}"
-        )
-        usingBuildFile(buildFile).withTasks("copyIt").run()
-        testFile('dest').assertHasDescendants(
-                'two/one.a',
-                'two/two.a',
-        )
-    }
-
-    /*
-     * two.a starts off with "$one\n${one+1}\n${one+1+1}\n"
-     * If these filters are chained in the correct order, you should get 6, 11, and 16
-     */
-    @Test public void copyMultipleFilterTest() {
-        TestFile buildFile = testFile('build.gradle').writelns(
-                """task (copy, type:Copy) {
-                   into 'dest'
-                   expand(one: 1)
-                   filter { (Integer.parseInt(it) * 10) as String }
-                   filter { (Integer.parseInt(it) + 2) as String }
-                   from('src/two/two.a') {
-                     filter { (Integer.parseInt(it) / 2) as String }
-                   }
-                }
-                """
-        )
-        usingBuildFile(buildFile).withTasks("copy").run()
-        Iterator<String> it = testFile('dest/two.a').readLines().iterator()
-        assertThat(it.next(), startsWith('6'))
-        assertThat(it.next(), startsWith('11'))
-        assertThat(it.next(), startsWith('16'))
-    }
-
-    @Test public void chainedTransformations() {
-        def buildFile = testFile('build.gradle') << '''
-            task copy(type: Copy) {
-                into 'dest'
-                rename '(.*).a', '\$1.renamed'
-                eachFile { fcd -> if (fcd.path.contains('/ignore/')) { fcd.exclude() } }
-                eachFile { fcd -> if (fcd.relativePath.segments.length > 1) { fcd.relativePath = fcd.relativePath.prepend('prefix') }}
-                filter(org.apache.tools.ant.filters.PrefixLines, prefix: 'line: ')
-                eachFile { fcd -> fcd.filter { it.replaceAll('^line:', 'prefix:') } }
-                from ('src') {
-                    rename '(.*).renamed', '\$1.renamed_twice'
-                    eachFile { fcd -> fcd.path = fcd.path.replaceAll('/one/sub/', '/one_sub/') }
-                    eachFile { fcd -> if (fcd.path.contains('/two/')) { fcd.exclude() } }
-                    eachFile { fcd -> fcd.filter { "[$it]" } }
-                }
-            }
-'''
-        usingBuildFile(buildFile).withTasks('copy').run()
-        testFile('dest').assertHasDescendants(
-                'root.renamed_twice',
-                'root.b',
-                'prefix/one/one.renamed_twice',
-                'prefix/one/one.b',
-                'prefix/one_sub/onesub.renamed_twice',
-                'prefix/one_sub/onesub.b'
-        )
-
-        Iterator<String> it = testFile('dest/root.renamed_twice').readLines().iterator()
-        assertThat(it.next(), equalTo('[prefix: line 1]'))
-        assertThat(it.next(), equalTo('[prefix: line 2]'))
-    }
-
-    @Test public void testCopyFromFileTree() {
-        TestFile buildFile = testFile("build.gradle").writelns(
-                """task cpy << {
-                   copy {
-                        from fileTree(dir: 'src', excludes: ['**/ignore/**'], includes: ['*', '*/*'])
-                        into 'dest'
-                    }
-                }"""
-        )
-        usingBuildFile(buildFile).withTasks("cpy").run()
-        testFile('dest').assertHasDescendants(
-                'root.a',
-                'root.b',
-                'one/one.a',
-                'one/one.b',
-                'two/two.a',
-                'two/two.b',
-        )
-    }
-
-    @Test public void testCopyFromFileCollection() {
-        TestFile buildFile = testFile("build.gradle").writelns(
-                """task copy << {
-                   copy {
-                        from files('src')
-                        into 'dest'
-                        exclude '**/ignore/**'
-                        exclude '*/*/*/**'
-                    }
-                }"""
-        )
-        usingBuildFile(buildFile).withTasks("copy").run()
-        testFile('dest').assertHasDescendants(
-                'root.a',
-                'root.b',
-                'one/one.a',
-                'one/one.b',
-                'two/two.a',
-                'two/two.b',
-        )
-    }
-
-    @Test public void testCopyFromCompositeFileCollection() {
-        testFile('a.jar').touch()
-
-        TestFile buildFile = testFile("build.gradle").writelns(
-                """
-                configurations { compile }
-                dependencies { compile files('a.jar') }
-                task copy << {
-                   copy {
-                        from files('src2') + fileTree { from 'src'; exclude '**/ignore/**' } + configurations.compile
-                        into 'dest'
-                        include { fte -> fte.relativePath.segments.length < 3 && (fte.file.directory || fte.file.name.contains('a')) }
-                    }
-                }"""
-        )
-        usingBuildFile(buildFile).withTasks("copy").run()
-        testFile('dest').assertHasDescendants(
-                'root.a',
-                'one/one.a',
-                'two/two.a',
-                'three/three.a',
-                'a.jar'
-        )
-    }
-
-    @Test public void testCopyWithCopyspec() {
-        TestFile buildFile = testFile("build.gradle").writelns(
-                """
-                def spec = copySpec {
-                    from 'src'
-                    exclude '**/ignore/**'
-                    include '*/*.a'
-                    into 'subdir'
-                }
-                task copy(type: Copy) {
-                    into 'dest'
-                    with spec
-                }"""
-        )
-        usingBuildFile(buildFile).withTasks("copy").run()
-        testFile('dest').assertHasDescendants(
-                'subdir/one/one.a',
-                'subdir/two/two.a'
-        )
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/CrossVersionCompatibilityIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/CrossVersionCompatibilityIntegrationTest.groovy
deleted file mode 100644
index 6ddccb5..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/CrossVersionCompatibilityIntegrationTest.groovy
+++ /dev/null
@@ -1,82 +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.integtests.fixtures.BasicGradleDistribution
-import org.gradle.integtests.fixtures.GradleDistribution
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.util.Jvm
-import org.junit.Rule
-import org.junit.Test
-
-class CrossVersionCompatibilityIntegrationTest {
-    @Rule public final GradleDistribution dist = new GradleDistribution()
-    @Rule public final TestResources resources = new TestResources()
-    private final BasicGradleDistribution gradle08 = dist.previousVersion('0.8')
-    private final BasicGradleDistribution gradle09rc1 = dist.previousVersion('0.9-rc-1')
-    private final BasicGradleDistribution gradle09rc2 = dist.previousVersion('0.9-rc-2')
-    private final BasicGradleDistribution gradle09rc3 = dist.previousVersion('0.9-rc-3')
-    private final BasicGradleDistribution gradle09 = dist.previousVersion('0.9')
-    private final BasicGradleDistribution gradle091 = dist.previousVersion('0.9.1')
-
-    @Test
-    public void canBuildJavaProject() {
-        dist.testFile('buildSrc/src/main/groovy').assertIsDir()
-
-        // Upgrade and downgrade from previous version to current version and back again
-        eachVersion([gradle08, gradle09rc1, gradle09rc2, gradle09rc3, gradle09, gradle091]) { version ->
-            version.executer().inDirectory(dist.testDir).withTasks('build').run()
-            dist.executer().inDirectory(dist.testDir).withTasks('build').run()
-            version.executer().inDirectory(dist.testDir).withTasks('build').run()
-        }
-    }
-
-    @Test
-    public void canUseWrapperFromPreviousVersionToRunCurrentVersion() {
-        eachVersion([gradle09rc1, gradle09rc2, gradle09rc3, gradle09, gradle091]) { version ->
-            checkWrapperWorksWith(version, dist)
-        }
-    }
-
-    @Test
-    public void canUseWrapperFromCurrentVersionToRunPreviousVersion() {
-        eachVersion([gradle09rc1, gradle09rc2, gradle09rc3, gradle09, gradle091]) { version ->
-            checkWrapperWorksWith(dist, version)
-        }
-    }
-
-    def checkWrapperWorksWith(BasicGradleDistribution wrapperGenVersion, BasicGradleDistribution executionVersion) {
-        wrapperGenVersion.executer().withTasks('wrapper').withArguments("-PdistZip=$executionVersion.binDistribution.absolutePath", "-PdistVersion=$executionVersion.version").run()
-        def result = wrapperGenVersion.executer().usingExecutable('gradlew').withTasks('hello').run()
-        assert result.output.contains("hello from $executionVersion.version")
-    }
-
-    def eachVersion(Iterable<BasicGradleDistribution> versions, Closure cl) {
-        versions.each { version ->
-            if (!version.worksWith(Jvm.current())) {
-                System.out.println("skipping $version as it does not work with ${Jvm.current()}.")
-                return
-            }
-            try {
-                System.out.println("building using $version");
-                cl.call(version)
-            } catch (Throwable t) {
-                throw new RuntimeException("Could not build test project using $version.", t)
-            }
-        }
-    }
-}
-
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/DistributionIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/DistributionIntegrationTest.groovy
deleted file mode 100644
index 46fa26d..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/DistributionIntegrationTest.groovy
+++ /dev/null
@@ -1,127 +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.apache.tools.ant.taskdefs.Expand
-import org.gradle.integtests.fixtures.GradleDistribution
-import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.gradle.util.AntUtil
-import org.gradle.util.GradleVersion
-import org.gradle.util.TestFile
-import org.junit.Rule
-import org.junit.Test
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
-
-class DistributionIntegrationTest {
-    @Rule public final GradleDistribution dist = new GradleDistribution()
-    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
-    private String version = new GradleVersion().version
-
-    @Test
-    public void binZipContents() {
-        TestFile binZip = dist.distributionsDir.file("gradle-$version-bin.zip")
-        binZip.usingNativeTools().unzipTo(dist.testDir)
-        TestFile contentsDir = dist.testDir.file("gradle-$version")
-
-        checkMinimalContents(contentsDir)
-
-        // Extra stuff
-        contentsDir.file('src').assertDoesNotExist()
-        contentsDir.file('samples').assertDoesNotExist()
-        contentsDir.file('docs').assertDoesNotExist()
-    }
-
-    @Test
-    public void allZipContents() {
-        TestFile binZip = dist.distributionsDir.file("gradle-$version-all.zip")
-        binZip.usingNativeTools().unzipTo(dist.testDir)
-        TestFile contentsDir = dist.testDir.file("gradle-$version")
-
-        checkMinimalContents(contentsDir)
-
-        // Source
-        contentsDir.file('src/org/gradle/api/Project.java').assertIsFile()
-        contentsDir.file('src/org/gradle/initialization/defaultBuildSourceScript.txt').assertIsFile()
-        contentsDir.file('src/org/gradle/gradleplugin/userinterface/swing/standalone/BlockingApplication.java').assertIsFile()
-        contentsDir.file('src/org/gradle/wrapper/Wrapper.java').assertIsFile()
-
-        // Samples
-        contentsDir.file('samples/java/quickstart/build.gradle').assertIsFile()
-
-        // Docs
-        contentsDir.file('docs/javadoc/index.html').assertIsFile()
-        contentsDir.file('docs/javadoc/org/gradle/api/Project.html').assertIsFile()
-        contentsDir.file('docs/groovydoc/index.html').assertIsFile()
-        contentsDir.file('docs/groovydoc/org/gradle/api/Project.html').assertIsFile()
-        contentsDir.file('docs/groovydoc/org/gradle/api/tasks/bundling/Zip.html').assertIsFile()
-        contentsDir.file('docs/userguide/userguide.html').assertIsFile()
-        contentsDir.file('docs/userguide/userguide_single.html').assertIsFile()
-//        contentsDir.file('docs/userguide/userguide.pdf').assertIsFile()
-    }
-
-    private def checkMinimalContents(TestFile contentsDir) {
-        // Check it can be executed
-        executer.inDirectory(contentsDir).usingExecutable('bin/gradle').withTaskList().run()
-
-        // Scripts
-        contentsDir.file('bin/gradle').assertIsFile()
-        contentsDir.file('bin/gradle.bat').assertIsFile()
-
-        // Top level files
-        contentsDir.file('LICENSE').assertIsFile()
-
-        // Libs
-        assertIsGradleJar(contentsDir.file("lib/gradle-core-${version}.jar"))
-        assertIsGradleJar(contentsDir.file("lib/gradle-ui-${version}.jar"))
-        assertIsGradleJar(contentsDir.file("lib/gradle-launcher-${version}.jar"))
-        assertIsGradleJar(contentsDir.file("lib/gradle-tooling-api-${version}.jar"))
-        assertIsGradleJar(contentsDir.file("lib/gradle-wrapper-${version}.jar"))
-        assertIsGradleJar(contentsDir.file("lib/plugins/gradle-code-quality-${version}.jar"))
-        assertIsGradleJar(contentsDir.file("lib/plugins/gradle-plugins-${version}.jar"))
-        assertIsGradleJar(contentsDir.file("lib/plugins/gradle-jetty-${version}.jar"))
-
-        // Docs
-        contentsDir.file('getting-started.html').assertIsFile()
-    }
-
-    private def assertIsGradleJar(TestFile jar) {
-        jar.assertIsFile()
-        assertThat(jar.manifest.mainAttributes.getValue('Implementation-Version'), equalTo(version))
-        assertThat(jar.manifest.mainAttributes.getValue('Implementation-Title'), equalTo('Gradle'))
-    }
-
-    @Test
-    public void sourceZipContents() {
-        TestFile srcZip = dist.distributionsDir.file("gradle-$version-src.zip")
-        srcZip.usingNativeTools().unzipTo(dist.testDir)
-        TestFile contentsDir = dist.testDir.file("gradle-$version")
-
-        // Build self using wrapper in source distribution
-        executer.inDirectory(contentsDir).usingExecutable('gradlew').withTasks('binZip').run()
-
-        File binZip = contentsDir.file('build/distributions').listFiles()[0]
-        Expand unpack = new Expand()
-        unpack.src = binZip
-        unpack.dest = contentsDir.file('build/distributions/unzip')
-        AntUtil.execute(unpack)
-        TestFile unpackedRoot = new TestFile(contentsDir.file('build/distributions/unzip').listFiles()[0])
-
-        // Make sure the build distribution does something useful
-        unpackedRoot.file("bin/gradle").assertIsFile()
-        // todo run something with the gradle build by the source dist
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/EclipseIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/EclipseIntegrationTest.groovy
deleted file mode 100644
index 501d8db..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/EclipseIntegrationTest.groovy
+++ /dev/null
@@ -1,126 +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.integtests.fixtures.TestResources
-import org.junit.Rule
-import org.junit.Test
-
-class EclipseIntegrationTest extends AbstractIdeIntegrationTest {
-    @Rule
-    public final TestResources testResources = new TestResources()
-
-    @Test
-    void canCreateAndDeleteMetaData() {
-        File buildFile = testFile("master/build.gradle")
-        usingBuildFile(buildFile).run()
-    }
-
-    @Test
-    void sourceEntriesInClasspathFileAreSortedAsPerUsualConvention() {
-        def expectedOrder = [
-            "src/main/java",
-            "src/main/groovy",
-            "src/main/resources",
-            "src/test/java",
-            "src/test/groovy",
-            "src/test/resources",
-            "src/integTest/java",
-            "src/integTest/groovy",
-            "src/integTest/resources"
-        ]
-
-        expectedOrder.each { testFile(it).mkdirs() }
-
-        def buildFile = testFile("build.gradle")
-        buildFile << """
-apply plugin: "java"
-apply plugin: "groovy"
-apply plugin: "eclipse"
-
-sourceSets {
-    integTest {
-        resources { srcDir "src/integTest/resources" }
-        java { srcDir "src/integTest/java" }
-        groovy { srcDir "src/integTest/groovy" }
-    }
-}
-        """
-
-        usingBuildFile(buildFile).withTasks("eclipse").run()
-
-        def classpath = parseClasspathFile()
-        def sourceEntries = findEntries(classpath, "src")
-        assert sourceEntries*. at path == expectedOrder
-    }
-
-    @Test
-    void outputDirDefaultsToEclipseDefault() {
-        def buildFile = testFile("build.gradle")
-        buildFile << "apply plugin: 'java'; apply plugin: 'eclipse'"
-
-        usingBuildFile(buildFile).withTasks("eclipse").run()
-
-        def classpath = parseClasspathFile()
-
-        def outputs = findEntries(classpath, "output")
-        assert outputs*. at path == ["bin"]
-
-        def sources = findEntries(classpath, "src")
-        sources.each { assert !it.attributes().containsKey("path") }
-    }
-
-    @Test
-    void canHandleCircularModuleDependencies() {
-        def repoDir = file("repo")
-        def artifact1 = publishArtifact(repoDir, "myGroup", "myArtifact1", "myArtifact2")
-        def artifact2 = publishArtifact(repoDir, "myGroup", "myArtifact2", "myArtifact1")
-
-        def buildFile = testFile("build.gradle")
-        buildFile << """
-apply plugin: "java"
-apply plugin: "eclipse"
-
-repositories {
-    mavenRepo urls: "${repoDir.toURI()}"
-}
-
-dependencies {
-    compile "myGroup:myArtifact1:1.0"
-}
-        """
-
-        usingBuildFile(buildFile).withTasks("eclipse").run()
-
-        def classpath = parseClasspathFile()
-        def libs = findEntries(classpath, "lib")
-        assert libs.size() == 2
-        assert libs*. at path*.text().collect { new File(it).name } as Set == [artifact1.name, artifact2.name] as Set
-    }
-
-    private parseClasspathFile(print = false) {
-        parseXmlFile(".classpath", print)
-    }
-
-    private parseProjectFile(print = false) {
-        parseXmlFile(".project", print)
-    }
-
-    private findEntries(classpath, kind) {
-        classpath.classpathentry.findAll { it. at kind == kind }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/ExecIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/ExecIntegrationTest.groovy
deleted file mode 100644
index e2e8932..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/ExecIntegrationTest.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.integtests
-
-import org.gradle.integtests.fixtures.TestResources
-import org.junit.Rule
-import org.junit.Test
-
-class ExecIntegrationTest extends AbstractIntegrationTest {
-    @Rule
-    public final TestResources testResources = new TestResources()
-
-    @Test
-    public void canExecuteJava() {
-        File buildFile = testFile("canExecuteJava.gradle");
-        usingBuildFile(buildFile).run();
-    }
-
-    @Test
-    public void canExecuteCommands() {
-        File buildFile = testFile("canExecuteCommands.gradle");
-        usingBuildFile(buildFile).run();
-    }
-
-
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/ExternalPluginIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/ExternalPluginIntegrationTest.groovy
deleted file mode 100644
index f539583..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/ExternalPluginIntegrationTest.groovy
+++ /dev/null
@@ -1,68 +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.integtests.fixtures.ArtifactBuilder
-import org.junit.Test
-
-public class ExternalPluginIntegrationTest extends AbstractIntegrationTest {
-    @Test
-    public void canReferencePluginInBuildSrcProjectById() {
-        testFile('buildSrc/src/main/java/CustomPlugin.java') << '''
-import org.gradle.api.*;
-public class CustomPlugin implements Plugin<Project> {
-    public void apply(Project p) { p.setProperty("prop", "value"); }
-}
-'''
-        testFile('buildSrc/src/main/resources/META-INF/gradle-plugins/custom.properties') << '''
-implementation-class=CustomPlugin
-'''
-
-        testFile('build.gradle') << '''
-apply plugin: 'custom'
-assert 'value' == prop
-task test
-'''
-        inTestDirectory().withTasks('test').run()
-    }
-    
-    @Test
-    public void canReferencePluginInExternalJarById() {
-        ArtifactBuilder builder = artifactBuilder()
-        builder.sourceFile('CustomPlugin.java') << '''
-import org.gradle.api.*;
-public class CustomPlugin implements Plugin<Project> {
-    public void apply(Project p) { p.setProperty("prop", "value"); }
-}
-'''
-        builder.resourceFile('META-INF/gradle-plugins/custom.properties') << '''
-implementation-class=CustomPlugin
-'''
-        builder.buildJar(testFile('external.jar'))
-
-        testFile('build.gradle') << '''
-buildscript {
-    dependencies {
-        classpath files('external.jar')
-    }
-}
-apply plugin: 'custom'
-assert 'value' == prop
-task test
-'''
-        inTestDirectory().withTasks('test').run()
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/ExternalScriptErrorIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/ExternalScriptErrorIntegrationTest.groovy
deleted file mode 100644
index 533f7f4..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/ExternalScriptErrorIntegrationTest.groovy
+++ /dev/null
@@ -1,89 +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.integtests.fixtures.ExecutionFailure
-import org.gradle.util.TestFile
-import org.junit.Test
-
-class ExternalScriptErrorIntegrationTest extends AbstractIntegrationTest {
-    @Test
-    public void reportsScriptEvaluationFailsWithGroovyException() {
-        testFile('build.gradle') << '''
-apply { from 'other.gradle' }
-'''
-        TestFile script = testFile('other.gradle') << '''
-
-doStuff()
-'''
-
-        ExecutionFailure failure = inTestDirectory().runWithFailure()
-
-        failure.assertHasFileName("Script '${script}'");
-        failure.assertHasLineNumber(3);
-        failure.assertHasDescription('A problem occurred evaluating script.');
-        failure.assertHasCause('Could not find method doStuff() for arguments [] on root project');
-    }
-
-    @Test
-    public void reportsScriptCompilationException() {
-        testFile('build.gradle') << '''
-apply { from 'other.gradle' }
-'''
-        TestFile script = testFile('other.gradle')
-        script.text = 'import org.gradle()'
-
-        ExecutionFailure failure = inTestDirectory().runWithFailure()
-        failure.assertHasFileName("Script '${script}'");
-        failure.assertHasLineNumber(1);
-        failure.assertHasDescription("Could not compile script '${script}'");
-    }
-
-    @Test
-    public void reportsMissingScript() {
-        TestFile buildScript = testFile('build.gradle') << '''
-apply { from 'unknown.gradle' }
-'''
-        TestFile script = testFile('unknown.gradle')
-
-        ExecutionFailure failure = inTestDirectory().runWithFailure()
-        failure.assertHasFileName("Build file '${buildScript}");
-        failure.assertHasLineNumber(2);
-        failure.assertHasDescription("A problem occurred evaluating root project");
-        failure.assertHasCause("Could not read script '${script}' as it does not exist.");
-    }
-
-    @Test
-    public void reportsTaskExecutionFailsWithRuntimeException() {
-        testFile('build.gradle') << '''
-apply { from 'other.gradle' }
-'''
-        TestFile script = testFile('other.gradle') << '''
-task doStuff << {
-    throw new RuntimeException('fail')
-}
-'''
-
-        ExecutionFailure failure = inTestDirectory().withTasks('doStuff').runWithFailure()
-
-        failure.assertHasFileName("Script '${script}'");
-        failure.assertHasLineNumber(3);
-        failure.assertHasDescription('Execution failed for task \':doStuff\'');
-        failure.assertHasCause('fail');
-    }
-
-}
-
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/ExternalScriptExecutionIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/ExternalScriptExecutionIntegrationTest.groovy
deleted file mode 100644
index e7cae95..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/ExternalScriptExecutionIntegrationTest.groovy
+++ /dev/null
@@ -1,184 +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.integtests.fixtures.ArtifactBuilder
-import org.gradle.integtests.fixtures.ExecutionResult
-import org.gradle.integtests.fixtures.HttpServer
-import org.gradle.util.TestFile
-import org.junit.Test
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
-
-public class ExternalScriptExecutionIntegrationTest extends AbstractIntegrationTest {
-    @Test
-    public void executesExternalScriptAgainstAProjectWithCorrectEnvironment() {
-        createExternalJar()
-        createBuildSrc()
-
-        TestFile externalScript = testFile('external.gradle')
-        externalScript << """
-            buildscript {
-                dependencies { classpath files('repo/test-1.3.jar') }
-            }
-            new org.gradle.test.BuildClass()
-            new BuildSrcClass()
-            println 'quiet message'
-            captureStandardOutput(LogLevel.ERROR)
-            println 'error message'
-            assert project != null
-            assert "${externalScript.absolutePath.replace("\\", "\\\\")}" == buildscript.sourceFile as String
-            assert "${externalScript.toURI()}" == buildscript.sourceURI as String
-            assert buildscript.classLoader == getClass().classLoader.parent
-            assert buildscript.classLoader == Thread.currentThread().contextClassLoader
-            assert gradle.scriptClassLoader == buildscript.classLoader.parent
-            assert project.buildscript.classLoader != buildscript.classLoader
-            task doStuff
-            someProp = 'value'
-"""
-        testFile('build.gradle') << '''
-apply { from 'external.gradle' }
-assert 'value' == someProp
-'''
-
-        ExecutionResult result = inTestDirectory().withTasks('doStuff').run()
-        assertThat(result.output, containsString('quiet message'))
-        assertThat(result.output, not(containsString('error message')))
-        assertThat(result.error, containsString('error message'))
-        assertThat(result.error, not(containsString('quiet message')))
-    }
-
-    @Test
-    public void canExecuteExternalScriptAgainstAnArbitraryObject() {
-        createBuildSrc()
-
-        testFile('external.gradle') << '''
-println 'quiet message'
-captureStandardOutput(LogLevel.ERROR)
-println 'error message'
-new BuildSrcClass()
-assert 'doStuff' == name
-assert buildscript.classLoader == getClass().classLoader.parent
-assert buildscript.classLoader == Thread.currentThread().contextClassLoader
-assert project.gradle.scriptClassLoader == buildscript.classLoader.parent
-assert project.buildscript.classLoader != buildscript.classLoader
-someProp = 'value'
-'''
-        testFile('build.gradle') << '''
-task doStuff
-apply {
-    to doStuff
-    from 'external.gradle'
-}
-assert 'value' == doStuff.someProp
-'''
-
-        ExecutionResult result = inTestDirectory().withTasks('doStuff').run()
-        assertThat(result.output, containsString('quiet message'))
-        assertThat(result.output, not(containsString('error message')))
-        assertThat(result.error, containsString('error message'))
-        assertThat(result.error, not(containsString('quiet message')))
-    }
-
-    @Test
-    public void canExecuteExternalScriptFromSettingsScript() {
-        testFile('settings.gradle') << ''' apply { from 'other.gradle' } '''
-        testFile('other.gradle') << ''' include 'child' '''
-        testFile('build.gradle') << ''' assert ['child'] == subprojects*.name '''
-
-        inTestDirectory().withTaskList().run()
-    }
-
-    @Test
-    public void canExecuteExternalScriptFromInitScript() {
-        TestFile initScript = testFile('init.gradle') << ''' apply { from 'other.gradle' } '''
-        testFile('other.gradle') << '''
-addListener(new ListenerImpl())
-class ListenerImpl extends BuildAdapter {
-    public void projectsEvaluated(Gradle gradle) {
-        gradle.rootProject.task('doStuff')
-    }
-}
-'''
-        inTestDirectory().usingInitScript(initScript).withTasks('doStuff').run()
-    }
-
-    @Test
-    public void canExecuteExternalScriptFromExternalScript() {
-        testFile('build.gradle') << ''' apply { from 'other1.gradle' } '''
-        testFile('other1.gradle') << ''' apply { from 'other2.gradle' } '''
-        testFile('other2.gradle') << ''' task doStuff '''
-
-        inTestDirectory().withTasks('doStuff').run()
-    }
-
-    @Test
-    public void canFetchScriptViaHttp() {
-        TestFile script = testFile('external.gradle')
-
-        HttpServer server = new HttpServer()
-        server.add('/external.gradle', script)
-        server.start()
-
-        script << """
-            task doStuff
-            assert buildscript.sourceFile == null
-            assert "http://localhost:$server.port/external.gradle" == buildscript.sourceURI as String
-"""
-
-        testFile('build.gradle') << """
-            apply from: 'http://localhost:$server.port/external.gradle'
-            defaultTasks 'doStuff'
-"""
-
-        inTestDirectory().run()
-
-        server.stop()
-    }
-
-    @Test
-    public void cachesScriptClassForAGivenScript() {
-        testFile('settings.gradle') << 'include \'a\', \'b\''
-        testFile('external.gradle') << 'appliedScript = this'
-        testFile('build.gradle') << '''
-allprojects {
-   apply from: "$rootDir/external.gradle"
-}
-subprojects {
-    assert appliedScript.class == rootProject.appliedScript.class
-}
-task doStuff
-'''
-        inTestDirectory().withTasks('doStuff').run()
-    }
-
-    private TestFile createBuildSrc() {
-        return testFile('buildSrc/src/main/java/BuildSrcClass.java') << '''
-            public class BuildSrcClass { }
-'''
-    }
-
-    private def createExternalJar() {
-        ArtifactBuilder builder = artifactBuilder();
-        builder.sourceFile('org/gradle/test/BuildClass.java') << '''
-            package org.gradle.test;
-            public class BuildClass { }
-'''
-        builder.buildJar(testFile("repo/test-1.3.jar"))
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/FileTreeCopyIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/FileTreeCopyIntegrationTest.groovy
deleted file mode 100644
index 4176690..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/FileTreeCopyIntegrationTest.groovy
+++ /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.integtests
-
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.util.TestFile
-import org.junit.Rule
-import org.junit.Test
-
-public class FileTreeCopyIntegrationTest extends AbstractIntegrationTest {
-    @Rule
-    public final TestResources resources = new TestResources("copyTestResources")
-
-    @Test public void testCopyWithClosure() {
-        TestFile buildFile = testFile("build.gradle").writelns(
-                """task cpy << {
-                   fileTree {
-                      from 'src'
-                      exclude '**/ignore/**'
-                   }.copy { into 'dest'}
-                }"""
-        )
-        usingBuildFile(buildFile).withTasks("cpy").run()
-        testFile('dest').assertHasDescendants(
-                'root.a',
-                'root.b',
-                'one/one.a',
-                'one/one.b',
-                'one/sub/onesub.a',
-                'one/sub/onesub.b',
-                'two/two.a',
-                'two/two.b',
-        )
-    }
-
-    @Test public void testCopyWithMap() {
-        TestFile buildFile = testFile("build.gradle").writelns(
-                """task cpy << {
-                   fileTree(dir:'src', excludes:['**/ignore/**', '**/sub/**']).copy { into 'dest'}
-                }"""
-        )
-        usingBuildFile(buildFile).withTasks("cpy").run()
-        testFile('dest').assertHasDescendants(
-                'root.a',
-                'root.b',
-                'one/one.a',
-                'one/one.b',
-                'two/two.a',
-                'two/two.b',
-        )
-    }
-
-    @Test public void testCopyFluent() {
-        TestFile buildFile = testFile("build.gradle").writelns(
-                """task cpy << {
-                   fileTree(dir:'src').exclude('**/ignore/**', '**/sub/*.?').copy { into 'dest' }
-                }"""
-        )
-        usingBuildFile(buildFile).withTasks("cpy").run()
-        testFile('dest').assertHasDescendants(
-                'root.a',
-                'root.b',
-                'one/one.a',
-                'one/one.b',
-                'two/two.a',
-                'two/two.b',
-        )
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/GroovyProjectIntegrationTest.java b/subprojects/core/src/integTest/groovy/org/gradle/integtests/GroovyProjectIntegrationTest.java
deleted file mode 100644
index 7c79eca..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/GroovyProjectIntegrationTest.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.integtests;
-
-import org.junit.Test;
-
-public class GroovyProjectIntegrationTest extends AbstractIntegrationTest {
-    @Test
-    public void handlesEmptyProject() {
-        testFile("build.gradle").writelns(
-                "apply plugin: 'groovy'"
-        );
-        inTestDirectory().withTasks("build").run();
-    }
-
-    @Test
-    public void handlesJavaSourceOnly() {
-        testFile("src/main/java/somepackage/SomeClass.java").writelns("public class SomeClass { }");
-        testFile("build.gradle").writelns("apply plugin: 'groovy'");
-        testFile("settings.gradle").write("rootProject.name='javaOnly'");
-        inTestDirectory().withTasks("build").run();
-        testFile("build/libs/javaOnly.jar").assertExists();
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/IdeaIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/IdeaIntegrationTest.groovy
deleted file mode 100644
index 47a58fc..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/IdeaIntegrationTest.groovy
+++ /dev/null
@@ -1,144 +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.custommonkey.xmlunit.Diff
-import org.custommonkey.xmlunit.ElementNameAndAttributeQualifier
-import org.custommonkey.xmlunit.XMLAssert
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.util.TestFile
-import org.junit.Rule
-import org.junit.Test
-import junit.framework.AssertionFailedError
-
-class IdeaIntegrationTest extends AbstractIdeIntegrationTest {
-    @Rule
-    public final TestResources testResources = new TestResources()
-
-    @Test
-    void canCreateAndDeleteMetaData() {
-        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
-    void worksWithAnEmptyProject() {
-        executer.withTasks('idea').run()
-
-        assertHasExpectedContents('root.ipr')
-        assertHasExpectedContents('root.iml')
-    }
-
-    @Test
-    void worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied() {
-        executer.withTasks('idea').run()
-
-        assertHasExpectedContents('root.ipr')
-    }
-
-    @Test
-    void worksWithNonStandardLayout() {
-        executer.inDirectory(testDir.file('root')).withTasks('idea').run()
-
-        assertHasExpectedContents('root/root.ipr')
-        assertHasExpectedContents('root/root.iml')
-        assertHasExpectedContents('top-level.iml')
-    }
-
-    @Test
-    void overwritesExistingDependencies() {
-        executer.withTasks('idea').run()
-
-        assertHasExpectedContents('root.iml')
-    }
-
-    @Test
-    void outputDirsDefaultToToIdeaDefaults() {
-        def settingsFile = file("settings.gradle")
-        settingsFile << "rootProject.name = 'root'"
-        def buildFile = file("build.gradle")
-        buildFile << "apply plugin: 'java'; apply plugin: 'idea'"
-
-        executer.usingSettingsFile(settingsFile).usingBuildScript(buildFile).withTasks("idea").run()
-
-        def module = parseImlFile("root")
-        def outputUrl = module.component.output[0]. at url
-        def testOutputUrl = module.component."output-test"[0]. at url
-
-        assert outputUrl.text() == 'file://$MODULE_DIR$/out/production/root'
-        assert testOutputUrl.text() == 'file://$MODULE_DIR$/out/test/root'
-    }
-
-    @Test
-    void canHandleCircularModuleDependencies() {
-        def repoDir = file("repo")
-        def artifact1 = publishArtifact(repoDir, "myGroup", "myArtifact1", "myArtifact2")
-        def artifact2 = publishArtifact(repoDir, "myGroup", "myArtifact2", "myArtifact1")
-
-        def settingsFile = file("settings.gradle")
-        settingsFile << "rootProject.name = 'root'"
-
-        def buildFile = file("build.gradle")
-        buildFile << """
-apply plugin: "java"
-apply plugin: "idea"
-
-repositories {
-    mavenRepo urls: "${repoDir.toURI()}"
-}
-
-dependencies {
-    compile "myGroup:myArtifact1:1.0"
-}
-        """
-
-        executer.usingSettingsFile(settingsFile).usingBuildScript(buildFile).withTasks("idea").run()
-
-        def module = parseImlFile("root", true)
-        def libs = module.component.orderEntry.library
-        assert libs.size() == 2
-        assert libs.CLASSES.root*. at url*.text().collect { new File(it).name } as Set == [artifact1.name + "!", artifact2.name + "!"] as Set
-    }
-
-    private void assertHasExpectedContents(String path) {
-        TestFile file = testDir.file(path).assertIsFile()
-        TestFile expectedFile = testDir.file("expectedFiles/${path}.xml").assertIsFile()
-
-        def cache = distribution.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())
-        try {
-            XMLAssert.assertXMLEqual(diff, true)
-        } catch (AssertionFailedError e) {
-            throw new AssertionFailedError("generated file '$path' does not contain the expected contents: ${e.message}.\nExpected:\n${expectedXml}\nActual:\n${file.text}").initCause(e)
-        }
-    }
-
-    private parseImlFile(projectName, print = false) {
-        parseXmlFile("${projectName}.iml", print)
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/IncrementalBuildIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/IncrementalBuildIntegrationTest.groovy
deleted file mode 100644
index 263596f..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/IncrementalBuildIntegrationTest.groovy
+++ /dev/null
@@ -1,378 +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.util.TestFile
-import org.junit.Test
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
-import org.gradle.integtests.fixtures.TestResources
-import org.junit.Rule
-
-class IncrementalBuildIntegrationTest extends AbstractIntegrationTest {
-    @Rule public final TestResources resource = new TestResources()
-
-    @Test
-    public void skipsTaskWhenOutputFileIsUpToDate() {
-        testFile('build.gradle') << '''
-task a(type: org.gradle.integtests.TransformerTask) {
-    inputFile = file('src.txt')
-    outputFile = file('src.a.txt')
-}
-task b(type: org.gradle.integtests.TransformerTask, dependsOn: a) {
-    inputFile = a.outputFile
-    outputFile = file('src.b.txt')
-}
-'''
-        TestFile inputFile = testFile('src.txt')
-        TestFile outputFileA = testFile('src.a.txt')
-        TestFile outputFileB = testFile('src.b.txt')
-
-        inputFile.text = 'content'
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
-
-        TestFile.Snapshot aSnapshot = outputFileA.snapshot()
-        TestFile.Snapshot bSnapshot = outputFileB.snapshot()
-        assertThat(outputFileA.text, equalTo('[content]'))
-        assertThat(outputFileB.text, equalTo('[[content]]'))
-
-        // No changes
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
-
-        outputFileA.assertHasNotChangedSince(aSnapshot)
-        outputFileB.assertHasNotChangedSince(bSnapshot)
-
-        // Update timestamp, no content changes
-
-        inputFile.setLastModified(inputFile.lastModified() - 10000);
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
-
-        outputFileA.assertHasNotChangedSince(aSnapshot)
-        outputFileB.assertHasNotChangedSince(bSnapshot)
-
-        // Change content
-
-        inputFile.text = 'new content'
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
-
-        outputFileA.assertHasChangedSince(aSnapshot)
-        outputFileB.assertHasChangedSince(bSnapshot)
-        assertThat(outputFileA.text, equalTo('[new content]'))
-        assertThat(outputFileB.text, equalTo('[[new content]]'))
-
-        // Delete intermediate output file
-
-        outputFileA.delete()
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':b')
-
-        assertThat(outputFileA.text, equalTo('[new content]'))
-        assertThat(outputFileB.text, equalTo('[[new content]]'))
-
-        // Delete final output file
-
-        outputFileB.delete()
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a')
-
-        assertThat(outputFileA.text, equalTo('[new content]'))
-        assertThat(outputFileB.text, equalTo('[[new content]]'))
-
-        // Change build file in a way which does not affect the task
-
-        testFile('build.gradle').text += '''
-task c
-'''
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
-
-        // Change an input property of the first task (the content format)
-
-        testFile('build.gradle').text += '''
-a.format = ' %s '
-'''
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
-
-        assertThat(outputFileA.text, equalTo(' new content '))
-        assertThat(outputFileB.text, equalTo('[ new content ]'))
-
-        // Change final output file destination
-
-        testFile('build.gradle').text += '''
-b.outputFile = file('new-output.txt')
-'''
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a')
-        outputFileB = testFile('new-output.txt')
-        outputFileB.assertIsFile()
-
-        // Run with --no-opt command-line options
-        inTestDirectory().withTasks('b').withArguments('--no-opt').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
-
-        // Output files already exist before using this version of Gradle
-        // delete .gradle dir to simulate this
-        testFile('.gradle').assertIsDir().deleteDir()
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
-
-        outputFileB.delete()
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a')
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
-    }
-
-    @Test
-    public void skipsTaskWhenOutputDirContentsAreUpToDate() {
-        testFile('build.gradle') << '''
-task a(type: org.gradle.integtests.DirTransformerTask) {
-    inputDir = file('src')
-    outputDir = file('build/a')
-}
-task b(type: org.gradle.integtests.DirTransformerTask, dependsOn: a) {
-    inputDir = a.outputDir
-    outputDir = file('build/b')
-}
-'''
-
-        testFile('src').createDir()
-        testFile('src/file1.txt').write('content')
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
-
-        TestFile outputAFile = testFile('build/a/file1.txt')
-        TestFile outputBFile = testFile('build/b/file1.txt')
-        TestFile.Snapshot aSnapshot = outputAFile.snapshot()
-        TestFile.Snapshot bSnapshot = outputBFile.snapshot()
-
-        outputAFile.assertContents(equalTo('[content]'))
-        outputBFile.assertContents(equalTo('[[content]]'))
-
-        // No changes
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
-
-        outputAFile.assertHasNotChangedSince(aSnapshot)
-        outputBFile.assertHasNotChangedSince(bSnapshot)
-
-        // Change content
-
-        testFile('src/file1.txt').write('new content')
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
-
-        outputAFile.assertHasChangedSince(aSnapshot)
-        outputBFile.assertHasChangedSince(bSnapshot)
-        outputAFile.assertContents(equalTo('[new content]'))
-        outputBFile.assertContents(equalTo('[[new content]]'))
-
-        // Add file
-
-        testFile('src/file2.txt').write('content2')
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
-
-        testFile('build/a/file2.txt').assertContents(equalTo('[content2]'))
-        testFile('build/b/file2.txt').assertContents(equalTo('[[content2]]'))
-
-        // Remove file
-
-        testFile('src/file1.txt').delete()
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':b')
-
-        // Output files already exist before using this version of Gradle
-        // delete .gradle dir to simulate this
-        testFile('.gradle').assertIsDir().deleteDir()
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
-
-        testFile('build/b').deleteDir()
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a')
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
-    }
-
-    @Test
-    public void skipsTaskWhenInputPropertiesHaveNotChanged() {
-        testFile('build.gradle') << '''
-task a(type: org.gradle.integtests.GeneratorTask) {
-    text = project.text
-    outputFile = file('dest.txt')
-}
-'''
-
-        inTestDirectory().withTasks('a').withArguments('-Ptext=text').run().assertTasksExecuted(':a').assertTasksSkipped()
-
-        inTestDirectory().withTasks('a').withArguments('-Ptext=text').run().assertTasksExecuted(':a').assertTasksSkipped(':a')
-
-        inTestDirectory().withTasks('a').withArguments('-Ptext=newtext').run().assertTasksExecuted(':a').assertTasksSkipped()
-    }
-
-    @Test
-    public void multipleTasksCanGenerateIntoOverlappingOutputDirectories() {
-        testFile('build.gradle') << '''
-task a(type: org.gradle.integtests.DirTransformerTask) {
-    inputDir = file('src/a')
-    outputDir = file('build')
-}
-task b(type: org.gradle.integtests.DirTransformerTask) {
-    inputDir = file('src/b')
-    outputDir = file('build')
-}
-'''
-
-        testFile('src/a/file1.txt') << 'content'
-        testFile('src/b/file2.txt') << 'content'
-
-        inTestDirectory().withTasks('a', 'b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
-
-        // No changes
-
-        inTestDirectory().withTasks('a', 'b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
-
-        // Delete an output file
-
-        testFile('build/file1.txt').delete()
-
-        inTestDirectory().withTasks('a', 'b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':b')
-
-        // Change an output file
-
-        testFile('build/file2.txt').write('something else')
-
-        inTestDirectory().withTasks('a', 'b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a')
-
-        // Change to new version of Gradle
-        // Simulate this by removing the .gradle dir
-        testFile('.gradle').assertIsDir().deleteDir()
-
-        inTestDirectory().withTasks('a', 'b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
-
-        testFile('build').deleteDir()
-
-        inTestDirectory().withTasks('a').run().assertTasksExecuted(':a').assertTasksSkipped()
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':b').assertTasksSkipped()
-    }
-
-    @Test
-    public void canUseUpToDatePredicateToForceTaskToExecute() {
-        testFile('build.gradle') << '''
-task inputsAndOutputs {
-    inputs.files 'src.txt'
-    outputs.files 'src.a.txt'
-    outputs.upToDateWhen { project.hasProperty('uptodate') }
-    doFirst {
-        outputs.files.singleFile.text = "[${inputs.files.singleFile.text}]"
-    }
-}
-task noOutputs {
-    inputs.files 'src.txt'
-    outputs.upToDateWhen { project.hasProperty('uptodate') }
-    doFirst { }
-}
-task nothing {
-    outputs.upToDateWhen { project.hasProperty('uptodate') }
-    doFirst { }
-}
-'''
-        TestFile srcFile = testFile('src.txt')
-        srcFile.text = 'content'
-
-        // Task with input files, output files and a predicate
-        inTestDirectory().withTasks('inputsAndOutputs').run().assertTasksExecuted(':inputsAndOutputs').assertTasksSkipped()
-
-        // Is up to date
-        inTestDirectory().withArguments('-Puptodate').withTasks('inputsAndOutputs').run().assertTasksExecuted(':inputsAndOutputs').assertTasksSkipped(':inputsAndOutputs')
-
-        // Changed input file
-        srcFile.text = 'different'
-        inTestDirectory().withArguments('-Puptodate').withTasks('inputsAndOutputs').run().assertTasksExecuted(':inputsAndOutputs').assertTasksSkipped()
-
-        // Predicate is false
-        inTestDirectory().withTasks('inputsAndOutputs').run().assertTasksExecuted(':inputsAndOutputs').assertTasksSkipped()
-
-        // Task with input files and a predicate
-        inTestDirectory().withTasks('noOutputs').run().assertTasksExecuted(':noOutputs').assertTasksSkipped()
-
-        // Is up to date
-        inTestDirectory().withArguments('-Puptodate').withTasks('noOutputs').run().assertTasksExecuted(':noOutputs').assertTasksSkipped(':noOutputs')
-
-        // Changed input file
-        srcFile.text = 'different again'
-        inTestDirectory().withArguments('-Puptodate').withTasks('noOutputs').run().assertTasksExecuted(':noOutputs').assertTasksSkipped()
-
-        // Predicate is false
-        inTestDirectory().withTasks('noOutputs').run().assertTasksExecuted(':noOutputs').assertTasksSkipped()
-
-        // Task a predicate only
-        inTestDirectory().withTasks('nothing').run().assertTasksExecuted(':nothing').assertTasksSkipped()
-
-        // Is up to date
-        inTestDirectory().withArguments('-Puptodate').withTasks('nothing').run().assertTasksExecuted(':nothing').assertTasksSkipped(':nothing')
-
-        // Predicate is false
-        inTestDirectory().withTasks('nothing').run().assertTasksExecuted(':nothing').assertTasksSkipped()
-    }
-
-    @Test
-    public void lifecycleTaskIsUpToDateWhenAllDependenciesAreSkipped() {
-        testFile('build.gradle') << '''
-task a(type: org.gradle.integtests.TransformerTask) {
-    inputFile = file('src.txt')
-    outputFile = file('out.txt')
-}
-task b(dependsOn: a)
-'''
-
-        testFile('src.txt').text = 'content'
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
-    }
-
-    @Test
-    public void canShareArtifactsBetweenBuilds() {
-        def buildFile = testFile('build.gradle') << '''
-task otherBuild(type: GradleBuild) {
-    buildFile = 'build.gradle'
-    tasks = ['generate']
-    startParameter.searchUpwards = false
-}
-task transform(type: org.gradle.integtests.TransformerTask) {
-    dependsOn otherBuild
-    inputFile = file('generated.txt')
-    outputFile = file('out.txt')
-}
-task generate(type: org.gradle.integtests.TransformerTask) {
-    inputFile = file('src.txt')
-    outputFile = file('generated.txt')
-}
-'''
-        testFile('settings.gradle') << 'rootProject.name = "build"'
-        testFile('src.txt').text = 'content'
-
-        usingBuildFile(buildFile).withTasks('transform').run().assertTasksExecuted(':otherBuild', ':build:generate', ':transform').assertTasksSkipped()
-        usingBuildFile(buildFile).withTasks('transform').run().assertTasksExecuted(':otherBuild', ':build:generate', ':transform').assertTasksSkipped(':transform', ':build:generate')
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest.groovy
deleted file mode 100644
index 08963dd..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest.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.integtests
-
-import org.gradle.integtests.fixtures.TestResources
-import org.junit.Rule
-import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.gradle.integtests.fixtures.GradleDistribution
-import org.junit.Test
-import org.gradle.integtests.fixtures.ExecutionFailure
-
-class IncrementalGroovyCompileIntegrationTest {
-    @Rule public final GradleDistribution distribution = new GradleDistribution()
-    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
-    @Rule public final TestResources resources = new TestResources()
-
-    @Test
-    public void recompilesSourceWhenPropertiesChange() {
-        executer.withTasks('compileGroovy').run().assertTasksSkipped()
-
-        distribution.testFile('build.gradle').text += '''
-            compileGroovy.options.debug = false
-'''
-
-        executer.withTasks('compileGroovy').run().assertTasksSkipped(':compileJava')
-
-        executer.withTasks('compileGroovy').run().assertTasksSkipped(':compileJava', ':compileGroovy')
-    }
-
-    @Test
-    public void recompilesDependentClasses() {
-        executer.withTasks("classes").run();
-
-        // Update interface, compile should fail
-        distribution.testFile('src/main/groovy/IPerson.groovy').assertIsFile().copyFrom(distribution.testFile('NewIPerson.groovy'))
-
-        ExecutionFailure failure = executer.withTasks("classes").runWithFailure();
-        failure.assertHasDescription("Execution failed for task ':compileGroovy'.");
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/IncrementalGroovyProjectBuildIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/IncrementalGroovyProjectBuildIntegrationTest.groovy
deleted file mode 100644
index a6e8121..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/IncrementalGroovyProjectBuildIntegrationTest.groovy
+++ /dev/null
@@ -1,55 +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.integtests.fixtures.GradleDistribution
-import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.gradle.util.TestFile
-import org.junit.Rule
-import org.junit.Test
-
-class IncrementalGroovyProjectBuildIntegrationTest {
-    @Rule public final GradleDistribution distribution = new GradleDistribution()
-    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
-
-    @Test
-    public void doesNotRebuildGroovydocIfSourceHasNotChanged() {
-        distribution.testFile("src/main/groovy/BuildClass.java") << 'public class BuildClass { }'
-        distribution.testFile("build.gradle") << '''
-            apply plugin: 'groovy'
-            dependencies { groovy localGroovy() }
-            groovydoc {
-                link('http://java.sun.com/j2se/1.5.0/docs/api', 'java.,org.xml.,javax.,org.xml.')
-            }
-'''
-
-        executer.withTasks("groovydoc").run();
-
-        TestFile indexFile = distribution.testFile("build/docs/groovydoc/index.html");
-        indexFile.assertIsFile();
-        TestFile.Snapshot snapshot = indexFile.snapshot();
-
-        executer.withTasks("groovydoc").run().assertTasksSkipped(':groovydoc');
-
-        indexFile.assertHasNotChangedSince(snapshot);
-
-        distribution.testFile("build.gradle").append("groovydoc.link('http://java.sun.com/j2se/1.5.0/docs/api', 'java.')")
-
-        executer.withTasks("groovydoc").run().assertTasksNotSkipped(':groovydoc');
-
-        executer.withTasks("groovydoc").run().assertTasksSkipped(':groovydoc');
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/IncrementalScalaCompileIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/IncrementalScalaCompileIntegrationTest.groovy
deleted file mode 100644
index 5392cd2..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/IncrementalScalaCompileIntegrationTest.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.integtests
-
-import org.gradle.integtests.fixtures.GradleDistribution
-import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.gradle.integtests.fixtures.TestResources
-import org.junit.Rule
-import org.junit.Test
-import org.gradle.integtests.fixtures.ExecutionFailure
-
-class IncrementalScalaCompileIntegrationTest {
-    @Rule public final GradleDistribution distribution = new GradleDistribution()
-    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
-    @Rule public final TestResources resources = new TestResources()
-
-    @Test
-    public void recompilesSourceWhenPropertiesChange() {
-        executer.withTasks('compileScala').run().assertTasksSkipped()
-
-        distribution.testFile('build.gradle').text += '''
-            compileScala.options.debug = false
-'''
-
-        executer.withTasks('compileScala').run().assertTasksSkipped(':compileJava')
-
-        executer.withTasks('compileScala').run().assertTasksSkipped(':compileJava', ':compileScala')
-    }
-
-    @Test
-    public void recompilesDependentClasses() {
-        executer.withTasks("classes").run();
-
-        // Update interface, compile should fail
-        distribution.testFile('src/main/scala/IPerson.scala').assertIsFile().copyFrom(distribution.testFile('NewIPerson.scala'))
-
-        ExecutionFailure failure = executer.withTasks("classes").runWithFailure();
-        failure.assertHasDescription("Execution failed for task ':compileScala'.");
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/InitScriptErrorIntegrationTest.java b/subprojects/core/src/integTest/groovy/org/gradle/integtests/InitScriptErrorIntegrationTest.java
deleted file mode 100644
index f23c3ae..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/InitScriptErrorIntegrationTest.java
+++ /dev/null
@@ -1,47 +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.integtests.fixtures.ExecutionFailure;
-import org.gradle.util.TestFile;
-import org.junit.Test;
-
-public class InitScriptErrorIntegrationTest extends AbstractIntegrationTest {
-    @Test
-    public void reportsInitScriptEvaluationFailsWithGroovyException() {
-        TestFile initScript = testFile("init.gradle");
-        initScript.write("\ncreateTakk('do-stuff')");
-        ExecutionFailure failure = inTestDirectory().usingInitScript(initScript).runWithFailure();
-
-        failure.assertHasFileName(String.format("Initialization script '%s'", initScript));
-        failure.assertHasLineNumber(2);
-        failure.assertHasDescription("A problem occurred evaluating initialization script.");
-        failure.assertHasCause("No signature of method: org.gradle.invocation.DefaultGradle.createTakk() is applicable for argument types: (java.lang.String) values: [do-stuff]");
-    }
-
-    @Test
-    public void reportsGroovyCompilationException() {
-        TestFile initScript = testFile("init.gradle");
-        initScript.writelns(
-            "// a comment",
-            "import org.gradle.unknown.Unknown",
-            "new Unknown()");
-        ExecutionFailure failure = inTestDirectory().usingInitScript(initScript).runWithFailure();
-        failure.assertHasFileName(String.format("Initialization script '%s'", initScript));
-        failure.assertHasLineNumber(2);
-        failure.assertHasDescription(String.format("Could not compile initialization script '%s'.", initScript));
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/InitScriptExecutionIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/InitScriptExecutionIntegrationTest.groovy
deleted file mode 100644
index e068ffe..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/InitScriptExecutionIntegrationTest.groovy
+++ /dev/null
@@ -1,87 +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.integtests.fixtures.ArtifactBuilder
-import org.gradle.integtests.fixtures.ExecutionResult
-import org.gradle.util.TestFile
-import org.junit.Test
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
-
-class InitScriptExecutionIntegrationTest extends AbstractIntegrationTest {
-    @Test
-    public void executesInitScriptWithCorrectEnvironment() {
-        createExternalJar();
-
-        TestFile initScript = testFile('init.gradle')
-        initScript << '''
-initscript {
-    dependencies { classpath files('repo/test-1.3.jar') }
-}
-new org.gradle.test.BuildClass()
-println 'quiet message'
-captureStandardOutput(LogLevel.ERROR)
-println 'error message'
-assert gradle != null
-assert initscript.classLoader == getClass().classLoader.parent
-assert initscript.classLoader == Thread.currentThread().contextClassLoader
-assert scriptClassLoader == initscript.classLoader.parent
-assert Gradle.class.classLoader == scriptClassLoader.parent.parent
-'''
-        testFile('build.gradle') << 'task doStuff'
-
-        ExecutionResult result = inTestDirectory().usingInitScript(initScript).withTasks('doStuff').run()
-        assertThat(result.output, containsString('quiet message'))
-        assertThat(result.output, not(containsString('error message')))
-        assertThat(result.error, containsString('error message'))
-        assertThat(result.error, not(containsString('quiet message')))
-    }
-
-    @Test
-    public void eachScriptHasIndependentClassLoader() {
-        createExternalJar()
-
-        TestFile initScript1 = testFile('init1.gradle')
-        initScript1 << '''
-initscript {
-    dependencies { classpath files('repo/test-1.3.jar') }
-}
-new org.gradle.test.BuildClass()
-'''
-        TestFile initScript2 = testFile('init2.gradle')
-        initScript2 << '''
-try {
-    Class.forName('org.gradle.test.BuildClass')
-    fail()
-} catch (ClassNotFoundException e) {
-}
-'''
-
-        testFile('build.gradle') << 'task doStuff'
-
-       inTestDirectory().usingInitScript(initScript1).usingInitScript(initScript2)
-    }
-
-    private def createExternalJar() {
-        ArtifactBuilder builder = artifactBuilder();
-        builder.sourceFile('org/gradle/test/BuildClass.java') << '''
-            package org.gradle.test;
-            public class BuildClass { }
-'''
-        builder.buildJar(testFile("repo/test-1.3.jar"))
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/IvyPublishIntegrationTest.java b/subprojects/core/src/integTest/groovy/org/gradle/integtests/IvyPublishIntegrationTest.java
deleted file mode 100644
index 9591831..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/IvyPublishIntegrationTest.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.integtests;
-
-import org.gradle.integtests.fixtures.GradleDistribution;
-import org.gradle.integtests.fixtures.GradleDistributionExecuter;
-import org.gradle.integtests.fixtures.Sample;
-import org.junit.Rule;
-import org.junit.Test;
-
-import java.io.File;
-
-/**
- * @author Hans Dockter
- */
-public class IvyPublishIntegrationTest {
-    @Rule
-    public final GradleDistribution dist = new GradleDistribution();
-    @Rule
-    public final GradleDistributionExecuter executer = new GradleDistributionExecuter();
-    @Rule
-    public final Sample sample = new Sample("ivypublish");
-
-    @Test
-    public void testResolve() {
-        // the actual testing is done in the build script.
-        File projectDir = sample.getDir();
-        executer.inDirectory(projectDir).withTasks("clean", "uploadArchives").run();
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/JUnitIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/JUnitIntegrationTest.groovy
deleted file mode 100644
index a06c5e9..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/JUnitIntegrationTest.groovy
+++ /dev/null
@@ -1,377 +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.util.TestFile
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.gradle.integtests.fixtures.*
-import static org.gradle.util.Matchers.*
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
-
-public class JUnitIntegrationTest {
-    @Rule public final GradleDistribution dist = new GradleDistribution()
-    @Rule public final TestResources resources = new TestResources()
-    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
-
-    @Test
-    public void executesTestsInCorrectEnvironment() {
-        TestFile testDir = dist.testDir;
-        executer.withTasks('build').run();
-
-        JUnitTestExecutionResult result = new JUnitTestExecutionResult(testDir)
-        result.assertTestClassesExecuted('org.gradle.OkTest', 'org.gradle.OtherTest')
-        result.testClass('org.gradle.OkTest').assertTestPassed('ok')
-        result.testClass('org.gradle.OkTest').assertStdout(containsString('This is test stdout'))
-        result.testClass('org.gradle.OkTest').assertStdout(containsString('no EOL'))
-        result.testClass('org.gradle.OkTest').assertStdout(containsString('class loaded'))
-        result.testClass('org.gradle.OkTest').assertStdout(containsString('test constructed'))
-        result.testClass('org.gradle.OkTest').assertStdout(containsString('stdout from another thread'))
-        result.testClass('org.gradle.OkTest').assertStderr(containsString('This is test stderr'))
-        result.testClass('org.gradle.OkTest').assertStderr(containsString('this is a warning'))
-        result.testClass('org.gradle.OtherTest').assertTestPassed('ok')
-        result.testClass('org.gradle.OtherTest').assertStdout(containsString('This is other stdout'))
-        result.testClass('org.gradle.OtherTest').assertStdout(containsString('other class loaded'))
-        result.testClass('org.gradle.OtherTest').assertStdout(containsString('other test constructed'))
-        result.testClass('org.gradle.OtherTest').assertStderr(containsString('This is other stderr'))
-        result.testClass('org.gradle.OtherTest').assertStderr(containsString('this is another warning'))
-    }
-
-    @Test
-    public void canRunJunit3Tests() {
-        executer.withTasks('check').withArguments('-PjunitVersion=4.8.1').run()
-
-        JUnitTestExecutionResult result = new JUnitTestExecutionResult(dist.testDir)
-        result.assertTestClassesExecuted('org.gradle.Test1')
-        result.testClass('org.gradle.Test1').assertTestPassed('testRenamesItself')
-
-        executer.withTasks('clean', 'check').withArguments('-PjunitVersion=3.8').run()
-
-        result = new JUnitTestExecutionResult(dist.testDir)
-        result.assertTestClassesExecuted('org.gradle.Test1')
-        result.testClass('org.gradle.Test1').assertTestPassed('testRenamesItself')
-    }
-
-    @Test
-    public void reportsAndBreaksBuildWhenTestFails() {
-        TestFile testDir = dist.getTestDir();
-        TestFile buildFile = testDir.file('build.gradle');
-        ExecutionFailure failure = executer.withTasks('build').runWithFailure();
-
-        failure.assertHasFileName("Build file '${buildFile}'");
-        failure.assertHasDescription("Execution failed for task ':test'.");
-        failure.assertThatCause(startsWith('There were failing tests.'));
-
-        assertThat(failure.getError(), containsLine('Test org.gradle.BrokenTest FAILED'));
-        assertThat(failure.getError(), containsLine('Test org.gradle.BrokenBefore FAILED'));
-        assertThat(failure.getError(), containsLine('Test org.gradle.BrokenAfter FAILED'));
-        assertThat(failure.getError(), containsLine('Test org.gradle.BrokenBeforeAndAfter FAILED'));
-        assertThat(failure.getError(), containsLine('Test org.gradle.BrokenBeforeClass FAILED'));
-        assertThat(failure.getError(), containsLine('Test org.gradle.BrokenAfterClass FAILED'));
-        assertThat(failure.getError(), containsLine('Test org.gradle.BrokenConstructor FAILED'));
-        assertThat(failure.getError(), containsLine('Test org.gradle.BrokenException FAILED'));
-        assertThat(failure.getError(), containsLine('Test org.gradle.Unloadable FAILED'));
-
-        JUnitTestExecutionResult result = new JUnitTestExecutionResult(testDir)
-        result.assertTestClassesExecuted(
-                'org.gradle.BrokenTest',
-                'org.gradle.BrokenBefore',
-                'org.gradle.BrokenAfter',
-                'org.gradle.BrokenBeforeClass',
-                'org.gradle.BrokenAfterClass',
-                'org.gradle.BrokenBeforeAndAfter',
-                'org.gradle.BrokenConstructor',
-                'org.gradle.BrokenException',
-                'org.gradle.Unloadable')
-        result.testClass('org.gradle.BrokenTest').assertTestFailed('failure', equalTo('java.lang.AssertionError: failed'))
-        result.testClass('org.gradle.BrokenTest').assertTestFailed('broken', equalTo('java.lang.IllegalStateException'))
-        result.testClass('org.gradle.BrokenBeforeClass').assertTestFailed('classMethod', equalTo('java.lang.AssertionError: failed'))
-        result.testClass('org.gradle.BrokenAfterClass').assertTestFailed('classMethod', equalTo('java.lang.AssertionError: failed'))
-        result.testClass('org.gradle.BrokenBefore').assertTestFailed('ok', equalTo('java.lang.AssertionError: failed'))
-        result.testClass('org.gradle.BrokenAfter').assertTestFailed('ok', equalTo('java.lang.AssertionError: failed'))
-        result.testClass('org.gradle.BrokenBeforeAndAfter').assertTestFailed('ok', equalTo('java.lang.AssertionError: before failed'), equalTo('java.lang.AssertionError: after failed'))
-        result.testClass('org.gradle.BrokenConstructor').assertTestFailed('ok', equalTo('java.lang.AssertionError: failed'))
-        result.testClass('org.gradle.BrokenException').assertTestFailed('broken', startsWith('Could not determine failure message for exception of type org.gradle.BrokenException$BrokenRuntimeException: '))
-        result.testClass('org.gradle.Unloadable').assertTestFailed('initializationError', equalTo('java.lang.AssertionError: failed'))
-    }
-
-    @Test
-    public void canRunSingleTests() {
-        executer.withTasks('test').withArguments('-Dtest.single=Ok2').run()
-        def result = new JUnitTestExecutionResult(dist.testDir)
-        result.assertTestClassesExecuted('Ok2')
-
-        executer.withTasks('cleanTest', 'test').withArguments('-Dtest.single=Ok').run()
-        result.assertTestClassesExecuted('Ok', 'Ok2')
-
-        def failure = executer.withTasks('test').withArguments('-Dtest.single=DoesNotMatchAClass').runWithFailure()
-        failure.assertHasCause('Could not find matching test for pattern: DoesNotMatchAClass')
-
-        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();
-        testDir.file('settings.gradle').write("include 'a', 'b'");
-        testDir.file('b/build.gradle') << '''
-            apply plugin: 'java'
-            repositories { mavenCentral() }
-            dependencies { compile 'junit:junit:4.7' }
-        '''
-        testDir.file('b/src/main/java/org/gradle/AbstractTest.java') << '''
-            package org.gradle;
-            public abstract class AbstractTest {
-                @org.junit.Test public void ok() { }
-            }
-        '''
-        TestFile buildFile = testDir.file('a/build.gradle');
-        buildFile << '''
-            apply plugin: 'java'
-            repositories { mavenCentral() }
-            dependencies { testCompile project(':b') }
-        '''
-        testDir.file('a/src/test/java/org/gradle/SomeTest.java') << '''
-            package org.gradle;
-            public class SomeTest extends AbstractTest {
-            }
-        '''
-
-        executer.withTasks('a:test').run();
-
-        JUnitTestExecutionResult result = new JUnitTestExecutionResult(testDir.file('a'))
-        result.assertTestClassesExecuted('org.gradle.SomeTest')
-        result.testClass('org.gradle.SomeTest').assertTestPassed('ok')
-    }
-
-    @Test
-    public void canExcludeSuperClassesFromExecution() {
-        TestFile testDir = dist.getTestDir();
-        TestFile buildFile = testDir.file('build.gradle');
-        buildFile << '''
-            apply plugin: 'java'
-            repositories { mavenCentral() }
-            dependencies { testCompile 'junit:junit:4.7' }
-            test { exclude '**/BaseTest.*' }
-        '''
-        testDir.file('src/test/java/org/gradle/BaseTest.java') << '''
-            package org.gradle;
-            public class BaseTest {
-                @org.junit.Test public void ok() { }
-            }
-        '''
-        testDir.file('src/test/java/org/gradle/SomeTest.java') << '''
-            package org.gradle;
-            public class SomeTest extends BaseTest {
-            }
-        '''
-
-        executer.withTasks('test').run();
-
-        JUnitTestExecutionResult result = new JUnitTestExecutionResult(testDir)
-        result.assertTestClassesExecuted('org.gradle.SomeTest')
-        result.testClass('org.gradle.SomeTest').assertTestPassed('ok')
-    }
-
-    @Test
-    public void detectsTestClasses() {
-        executer.withTasks('test').run()
-
-        JUnitTestExecutionResult result = new JUnitTestExecutionResult(dist.testDir)
-        result.assertTestClassesExecuted('org.gradle.EmptyRunWithSubclass', 'org.gradle.TestsOnInner', 'org.gradle.TestsOnInner$SomeInner')
-        result.testClass('org.gradle.EmptyRunWithSubclass').assertTestsExecuted('ok')
-        result.testClass('org.gradle.EmptyRunWithSubclass').assertTestPassed('ok')
-        result.testClass('org.gradle.TestsOnInner').assertTestPassed('ok')
-        result.testClass('org.gradle.TestsOnInner$SomeInner').assertTestPassed('ok')
-    }
-
-    @Test
-    public void runsAllTestsInTheSameForkedJvm() {
-        TestFile testDir = dist.getTestDir();
-        testDir.file('build.gradle').writelns(
-                "apply plugin: 'java'",
-                "repositories { mavenCentral() }",
-                "dependencies { compile 'junit:junit:4.7' }"
-        );
-        testDir.file('src/test/java/org/gradle/AbstractTest.java').writelns(
-                "package org.gradle;",
-                "public abstract class AbstractTest {",
-                "    @org.junit.Test public void ok() {",
-                "        long time = java.lang.management.ManagementFactory.getRuntimeMXBean().getStartTime();",
-                "        System.out.println(String.format(\"VM START TIME = %s\", time));",
-                "    }",
-                "}");
-        testDir.file('src/test/java/org/gradle/SomeTest.java').writelns(
-                "package org.gradle;",
-                "public class SomeTest extends AbstractTest {",
-                "}");
-        testDir.file('src/test/java/org/gradle/SomeTest2.java').writelns(
-                "package org.gradle;",
-                "public class SomeTest2 extends AbstractTest {",
-                "}");
-
-        executer.withTasks('test').run();
-
-        TestFile results1 = testDir.file('build/test-results/TEST-org.gradle.SomeTest.xml');
-        TestFile results2 = testDir.file('build/test-results/TEST-org.gradle.SomeTest2.xml');
-        results1.assertIsFile();
-        results2.assertIsFile();
-        assertThat(results1.linesThat(containsString('VM START TIME =')).get(0), equalTo(results2.linesThat(containsString('VM START TIME =')).get(0)));
-    }
-
-    @Test
-    public void canSpecifyMaximumNumberOfTestClassesToExecuteInAForkedJvm() {
-        TestFile testDir = dist.getTestDir();
-        testDir.file('build.gradle').writelns(
-                "apply plugin: 'java'",
-                "repositories { mavenCentral() }",
-                "dependencies { compile 'junit:junit:4.7' }",
-                "test.forkEvery = 1"
-        );
-        testDir.file('src/test/java/org/gradle/AbstractTest.java').writelns(
-                "package org.gradle;",
-                "public abstract class AbstractTest {",
-                "    @org.junit.Test public void ok() {",
-                "        long time = java.lang.management.ManagementFactory.getRuntimeMXBean().getStartTime();",
-                "        System.out.println(String.format(\"VM START TIME = %s\", time));",
-                "    }",
-                "}");
-        testDir.file('src/test/java/org/gradle/SomeTest.java').writelns(
-                "package org.gradle;",
-                "public class SomeTest extends AbstractTest {",
-                "}");
-        testDir.file('src/test/java/org/gradle/SomeTest2.java').writelns(
-                "package org.gradle;",
-                "public class SomeTest2 extends AbstractTest {",
-                "}");
-
-        executer.withTasks('test').run();
-
-        TestFile results1 = testDir.file('build/test-results/TEST-org.gradle.SomeTest.xml');
-        TestFile results2 = testDir.file('build/test-results/TEST-org.gradle.SomeTest2.xml');
-        results1.assertIsFile();
-        results2.assertIsFile();
-        assertThat(results1.linesThat(containsString('VM START TIME =')).get(0), not(equalTo(results2.linesThat(
-                containsString('VM START TIME =')).get(0))));
-    }
-
-    @Test
-    public void canListenForTestResults() {
-        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.junit.Test public void fail() { org.junit.Assert.fail(\"message\"); }",
-                "@org.junit.Test public void knownError() { throw new RuntimeException(\"message\"); }",
-                "@org.junit.Test public void unknownError() throws AppException { throw new AppException(); }",
-                "}"
-        );
-        testDir.file('src/test/java/SomeOtherTest.java').writelns(
-                "public class SomeOtherTest {",
-                "@org.junit.Test public void pass() { }",
-                "}"
-        );
-
-        testDir.file('build.gradle') << '''
-            apply plugin: 'java'
-            repositories { mavenCentral() }
-            dependencies { testCompile 'junit:junit:4.7' }
-            def listener = new TestListenerImpl()
-            test.addTestListener(listener)
-            test.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] [$result.resultType] [$result.testCount]" }
-                void beforeTest(TestDescriptor test) { println "START [$test] [$test.name]" }
-                void afterTest(TestDescriptor test, TestResult result) { println "FINISH [$test] [$test.name] [$result.resultType] [$result.testCount] [$result.exception]" }
-            }
-        '''
-
-        ExecutionResult result = executer.withTasks("test").run();
-        assertThat(result.getOutput(), containsLine("START [tests] []"));
-        assertThat(result.getOutput(), containsLine("FINISH [tests] [] [FAILURE] [4]"));
-
-        assertThat(result.getOutput(), containsLine("START [test process 'Gradle Worker 1'] [Gradle Worker 1]"));
-        assertThat(result.getOutput(), containsLine("FINISH [test process 'Gradle Worker 1'] [Gradle Worker 1] [FAILURE] [4]"));
-
-        assertThat(result.getOutput(), containsLine("START [test class SomeOtherTest] [SomeOtherTest]"));
-        assertThat(result.getOutput(), containsLine("FINISH [test class SomeOtherTest] [SomeOtherTest] [SUCCESS] [1]"));
-        assertThat(result.getOutput(), containsLine("START [test pass(SomeOtherTest)] [pass]"));
-        assertThat(result.getOutput(), containsLine("FINISH [test pass(SomeOtherTest)] [pass] [SUCCESS] [1] [null]"));
-
-        assertThat(result.getOutput(), containsLine("START [test class SomeTest] [SomeTest]"));
-        assertThat(result.getOutput(), containsLine("FINISH [test class SomeTest] [SomeTest] [FAILURE] [3]"));
-        assertThat(result.getOutput(), containsLine("START [test fail(SomeTest)] [fail]"));
-        assertThat(result.getOutput(), containsLine("FINISH [test fail(SomeTest)] [fail] [FAILURE] [1] [java.lang.AssertionError: message]"));
-        assertThat(result.getOutput(), containsLine("START [test knownError(SomeTest)] [knownError]"));
-        assertThat(result.getOutput(), containsLine("FINISH [test knownError(SomeTest)] [knownError] [FAILURE] [1] [java.lang.RuntimeException: message]"));
-        assertThat(result.getOutput(), containsLine("START [test unknownError(SomeTest)] [unknownError]"));
-        assertThat(result.getOutput(), containsLine("FINISH [test unknownError(SomeTest)] [unknownError] [FAILURE] [1] [org.gradle.messaging.remote.internal.PlaceholderException: AppException: null]"));
-    }
-
-    @Test
-    public void canListenForTestResultsWhenJUnit3IsUsed() {
-        TestFile testDir = dist.getTestDir();
-        testDir.file('src/test/java/SomeTest.java').writelns(
-                "public class SomeTest extends junit.framework.TestCase {",
-                "public void testPass() { }",
-                "public void testFail() { junit.framework.Assert.fail(\"message\"); }",
-                "public void testError() { throw new RuntimeException(\"message\"); }",
-                "}"
-        );
-
-        testDir.file('build.gradle') << '''
-            apply plugin: 'java'
-            repositories { mavenCentral() }
-            dependencies { testCompile 'junit:junit:3.8' }
-            def listener = new TestListenerImpl()
-            test.addTestListener(listener)
-            test.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.exception]" }
-            }
-        '''
-
-        ExecutionResult result = executer.withTasks("test").run();
-        assertThat(result.getOutput(), containsLine("START [test class SomeTest] [SomeTest]"));
-        assertThat(result.getOutput(), containsLine("FINISH [test class SomeTest] [SomeTest]"));
-        assertThat(result.getOutput(), containsLine("START [test testPass(SomeTest)] [testPass]"));
-        assertThat(result.getOutput(), containsLine("FINISH [test testPass(SomeTest)] [testPass] [null]"));
-        assertThat(result.getOutput(), containsLine("START [test testFail(SomeTest)] [testFail]"));
-        assertThat(result.getOutput(), containsLine("FINISH [test testFail(SomeTest)] [testFail] [junit.framework.AssertionFailedError: message]"));
-        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/core/src/integTest/groovy/org/gradle/integtests/JUnitTestExecutionResult.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/JUnitTestExecutionResult.groovy
deleted file mode 100644
index ed2f2e6..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/JUnitTestExecutionResult.groovy
+++ /dev/null
@@ -1,153 +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 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 JUnitTestExecutionResult implements TestExecutionResult {
-    private final TestFile buildDir
-
-    def JUnitTestExecutionResult(TestFile projectDir, String buildDirName = 'build') {
-        this.buildDir = projectDir.file(buildDirName)
-    }
-
-    TestExecutionResult assertTestClassesExecuted(String... testClasses) {
-        Map<String, File> classes = findClasses()
-        assertThat(classes.keySet(), equalTo(testClasses as Set));
-        this
-    }
-
-    TestClassExecutionResult testClass(String testClass) {
-        return new JUnitTestClassExecutionResult(findTestClass(testClass), testClass)
-    }
-
-    private def findTestClass(String testClass) {
-        def classes = findClasses()
-        assertThat(classes.keySet(), hasItem(testClass))
-        def classFile = classes.get(testClass)
-        assertThat(classFile, notNullValue())
-        return new XmlSlurper().parse(classFile)
-    }
-
-    private def findClasses() {
-        buildDir.file('test-results').assertIsDir()
-        buildDir.file('test-results/TESTS-TestSuites.xml').assertIsFile()
-        buildDir.file('reports/tests/index.html').assertIsFile()
-
-        Map<String, File> classes = [:]
-        buildDir.file('test-results').eachFile { File file ->
-            def matcher = (file.name =~ /TEST-(.+)\.xml/)
-            if (matcher.matches()) {
-                classes[matcher.group(1)] = file
-            }
-        }
-        return classes
-    }
-}
-
-class JUnitTestClassExecutionResult implements TestClassExecutionResult {
-    GPathResult testClassNode
-    String testClassName
-    boolean checked
-
-    def JUnitTestClassExecutionResult(GPathResult testClassNode, String testClassName) {
-        this.testClassNode = testClassNode
-        this.testClassName = testClassName
-    }
-
-    TestClassExecutionResult assertTestsExecuted(String... testNames) {
-        Map<String, Node> testMethods = findTests()
-        assertThat(testMethods.keySet(), equalTo(testNames as Set))
-        this
-    }
-
-    TestClassExecutionResult assertTestPassed(String name) {
-        Map<String, Node> testMethods = findTests()
-        assertThat(testMethods.keySet(), hasItem(name))
-        assertThat(testMethods[name].failure.size(), equalTo(0))
-        this
-    }
-
-    TestClassExecutionResult assertTestFailed(String name, Matcher<? super String>... messageMatchers) {
-        Map<String, Node> testMethods = findTests()
-        assertThat(testMethods.keySet(), hasItem(name))
-
-        def failures = testMethods[name].failure
-        assertThat("Expected ${messageMatchers.length} failures. Found: $failures", failures.size(), equalTo(messageMatchers.length))
-
-        for (int i = 0; i < messageMatchers.length; i++) {
-            assertThat(failures[i]. at message.text(), messageMatchers[i])
-        }
-        this
-    }
-
-    TestClassExecutionResult assertConfigMethodPassed(String name) {
-        throw new UnsupportedOperationException();
-    }
-
-    TestClassExecutionResult assertConfigMethodFailed(String name) {
-        throw new UnsupportedOperationException();
-    }
-
-    TestClassExecutionResult assertStdout(Matcher<? super String> matcher) {
-        def stdout = testClassNode.'system-out'[0].text();
-        assertThat(stdout, matcher)
-        this
-    }
-
-    TestClassExecutionResult assertStderr(Matcher<? super String> matcher) {
-        def stderr = testClassNode.'system-err'[0].text();
-        assertThat(stderr, matcher)
-        this
-    }
-
-    private def findTests() {
-        if (!checked) {
-            assertThat(testClassNode.name(), equalTo('testsuite'))
-            assertThat(testClassNode. at name.text(), equalTo(testClassName))
-            assertThat(testClassNode. at tests.text(), not(equalTo('')))
-            assertThat(testClassNode. at failures.text(), not(equalTo('')))
-            assertThat(testClassNode. at errors.text(), not(equalTo('')))
-            assertThat(testClassNode. at time.text(), not(equalTo('')))
-            assertThat(testClassNode. at timestamp.text(), not(equalTo('')))
-            assertThat(testClassNode. at hostname.text(), not(equalTo('')))
-            assertThat(testClassNode.properties.size(), equalTo(1))
-            testClassNode.testcase.each { node ->
-                assertThat(node. at classname.text(), equalTo(testClassName))
-                assertThat(node. at name.text(), not(equalTo('')))
-                assertThat(node. at time.text(), not(equalTo('')))
-                node.failure.each { failure ->
-                    assertThat(failure. at message.size(), equalTo(1))
-                    assertThat(failure. at type.text(), not(equalTo('')))
-                    assertThat(failure.text(), not(equalTo('')))
-                }
-            }
-            assertThat(testClassNode.'system-out'.size(), equalTo(1))
-            assertThat(testClassNode.'system-err'.size(), equalTo(1))
-            checked = true
-        }
-        Map testMethods = [:]
-        testClassNode.testcase.each { testMethods[it. at name.text()] = it }
-        return testMethods
-    }
-
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/JavaProjectIntegrationTest.java b/subprojects/core/src/integTest/groovy/org/gradle/integtests/JavaProjectIntegrationTest.java
deleted file mode 100644
index e8af5bf..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/JavaProjectIntegrationTest.java
+++ /dev/null
@@ -1,104 +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.integtests.fixtures.ExecutionFailure;
-import org.gradle.util.TestFile;
-import org.junit.Test;
-
-import java.io.IOException;
-
-public class JavaProjectIntegrationTest extends AbstractIntegrationTest {
-    @Test
-    public void handlesEmptyProject() {
-        testFile("build.gradle").writelns("apply plugin: 'java'");
-        inTestDirectory().withTasks("build").run();
-    }
-
-    @Test
-    public void compilationFailureBreaksBuild() {
-        TestFile buildFile = testFile("build.gradle");
-        buildFile.writelns("apply plugin: 'java'");
-        testFile("src/main/java/org/gradle/broken.java").write("broken");
-
-        ExecutionFailure failure = usingBuildFile(buildFile).withTasks("build").runWithFailure();
-
-        failure.assertHasFileName(String.format("Build file '%s'", buildFile));
-        failure.assertHasDescription("Execution failed for task ':compileJava'");
-        failure.assertHasCause("Compile failed; see the compiler error output for details.");
-    }
-
-    @Test
-    public void testCompilationFailureBreaksBuild() {
-        TestFile buildFile = testFile("build.gradle");
-        buildFile.writelns("apply plugin: 'java'");
-        testFile("src/main/java/org/gradle/ok.java").write("package org.gradle; class ok { }");
-        testFile("src/test/java/org/gradle/broken.java").write("broken");
-
-        ExecutionFailure failure = usingBuildFile(buildFile).withTasks("build").runWithFailure();
-
-        failure.assertHasFileName(String.format("Build file '%s'", buildFile));
-        failure.assertHasDescription("Execution failed for task ':compileTestJava'");
-        failure.assertHasCause("Compile failed; see the compiler error output for details.");
-    }
-
-    @Test
-    public void handlesTestSrcWhichDoesNotContainAnyTestCases() {
-        TestFile buildFile = testFile("build.gradle");
-        buildFile.writelns("apply plugin: 'java'");
-        testFile("src/test/java/org/gradle/NotATest.java").writelns("package org.gradle;", "public class NotATest {}");
-
-        usingBuildFile(buildFile).withTasks("build").run();
-    }
-
-    @Test
-    public void javadocGenerationFailureBreaksBuild() throws IOException {
-        TestFile buildFile = testFile("javadocs.gradle");
-        buildFile.write("apply plugin: 'java'");
-        testFile("src/main/java/org/gradle/broken.java").write("class Broken { }");
-
-        ExecutionFailure failure = usingBuildFile(buildFile).withTasks("javadoc").runWithFailure();
-
-        failure.assertHasFileName(String.format("Build file '%s'", buildFile));
-        failure.assertHasDescription("Execution failed for task ':javadoc'");
-        failure.assertHasCause("Javadoc generation failed.");
-    }
-
-    @Test
-    public void handlesResourceOnlyProject() throws IOException {
-        TestFile buildFile = testFile("resources.gradle");
-        buildFile.write("apply plugin: 'java'");
-        testFile("src/main/resources/org/gradle/resource.file").write("test resource");
-
-        usingBuildFile(buildFile).withTasks("build").run();
-        testFile("build/classes/main/org/gradle/resource.file").assertExists();
-    }
-
-    @Test
-    public void generatesArtifactsWhenVersionIsEmpty() {
-        testFile("settings.gradle").write("rootProject.name = 'empty'");
-        TestFile buildFile = testFile("build.gradle");
-        buildFile.writelns(
-                "apply plugin: 'java'",
-                "version = ''"
-        );
-        testFile("src/main/resources/org/gradle/resource.file").write("some resource");
-
-        usingBuildFile(buildFile).withTasks("jar").run();
-        testFile("build/libs/empty.jar").assertIsFile();
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/LoggingIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/LoggingIntegrationTest.groovy
deleted file mode 100644
index 68b3909..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/LoggingIntegrationTest.groovy
+++ /dev/null
@@ -1,343 +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.ExecutionResult
-import org.gradle.integtests.fixtures.GradleDistribution
-import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.gradle.util.TestFile
-import org.junit.Rule
-import org.junit.Test
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.integtests.fixtures.Sample
-import org.gradle.integtests.fixtures.UsesSample
-
-/**
- * @author Hans Dockter
- */
-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()
-
-    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'
-        )
-    }}
-
-    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(this.&run, logOutput.quiet)
-    }
-
-    @Test
-    public void lifecycleLogging() {
-        checkOutput(this.&run, logOutput.lifecycle)
-    }
-
-    @Test
-    public void infoLogging() {
-        checkOutput(this.&run, logOutput.info)
-    }
-
-    @Test
-    public void debugLogging() {
-        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)
-    }
-
-    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()
-    }
-
-    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)
-        }
-        forbiddenMessages.each {List messages ->
-            checkOuts(false, result.output, messages) {expected, actual-> actual.contains(expected)}
-            checkOuts(false, result.error, messages) {expected, actual-> actual.contains(expected)}
-        }
-    }
-
-    def checkOuts(boolean shouldContain, String result, List outs, Closure partialLine) {
-        outs.each {String expectedOut ->
-            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")
-            }
-        }
-    }
-}
-
-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/core/src/integTest/groovy/org/gradle/integtests/MultiprojectIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/MultiprojectIntegrationTest.groovy
deleted file mode 100644
index d3bca15..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/MultiprojectIntegrationTest.groovy
+++ /dev/null
@@ -1,47 +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.junit.Test
-
-class MultiProjectIntegrationTest extends AbstractIntegrationTest {
-    @Test
-    public void canInjectConfigurationFromParentProject() {
-        testFile('settings.gradle') << 'include "a", "b"'
-        testFile('build.gradle') << '''
-            allprojects {
-                def destDir = buildDir
-                task test << {
-                    destDir.mkdirs()
-                    new File(destDir, 'test.txt') << 'content'
-                }
-                gradle.taskGraph.whenReady {
-                    destDir.mkdirs()
-                    new File(destDir, 'whenReady.txt') << 'content'
-                }
-                afterEvaluate {
-                    destDir.mkdirs()
-                    new File(destDir, 'afterEvaluate.txt') << 'content'
-                }
-            }
-'''
-        inTestDirectory().withTasks('test').run()
-
-        testFile('build').assertHasDescendants('test.txt', 'whenReady.txt', 'afterEvaluate.txt')
-        testFile('a/build').assertHasDescendants('test.txt', 'whenReady.txt', 'afterEvaluate.txt')
-        testFile('b/build').assertHasDescendants('test.txt', 'whenReady.txt', 'afterEvaluate.txt')
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/OsgiProjectSampleIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/OsgiProjectSampleIntegrationTest.groovy
deleted file mode 100644
index 28de62a..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/OsgiProjectSampleIntegrationTest.groovy
+++ /dev/null
@@ -1,59 +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 java.util.jar.Manifest
-import org.gradle.integtests.fixtures.GradleDistribution
-import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.gradle.util.TestFile
-import org.junit.Rule
-import org.junit.Test
-import static org.junit.Assert.*
-import org.gradle.integtests.fixtures.Sample
-import org.gradle.util.GradleVersion
-
-/**
- * @author Hans Dockter
- */
-class OsgiProjectSampleIntegrationTest {
-    @Rule public final GradleDistribution dist = new GradleDistribution()
-    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
-    @Rule public final Sample sample = new Sample('osgi')
-
-    @Test
-    public void osgiProjectSamples() {
-        long start = System.currentTimeMillis()
-        TestFile osgiProjectDir = sample.dir
-        executer.inDirectory(osgiProjectDir).withTasks('clean', 'assemble').run()
-        TestFile tmpDir = dist.testDir
-        osgiProjectDir.file('build/libs/osgi-1.0.jar').unzipTo(tmpDir)
-        tmpDir.file('META-INF/MANIFEST.MF').withInputStream { InputStream instr ->
-            Manifest manifest = new Manifest(instr)
-            checkManifest(manifest, start)
-        }
-    }
-
-    static void checkManifest(Manifest manifest, start) {
-        assertEquals('Example Gradle Activator', manifest.mainAttributes.getValue('Bundle-Name'))
-        assertEquals('2', manifest.mainAttributes.getValue('Bundle-ManifestVersion'))
-        assertEquals('Bnd-0.0.384', manifest.mainAttributes.getValue('Tool'))
-        assertTrue(start <= Long.parseLong(manifest.mainAttributes.getValue('Bnd-LastModified')))
-        assertEquals('1.0', manifest.mainAttributes.getValue('Bundle-Version'))
-        assertEquals('gradle_tooling.osgi', manifest.mainAttributes.getValue('Bundle-SymbolicName'))
-        assertEquals( new GradleVersion().version, manifest.mainAttributes.getValue('Built-By'))
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/ProjectLoadingIntegrationTest.java b/subprojects/core/src/integTest/groovy/org/gradle/integtests/ProjectLoadingIntegrationTest.java
deleted file mode 100644
index fee6710..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/ProjectLoadingIntegrationTest.java
+++ /dev/null
@@ -1,253 +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.integtests.fixtures.ExecutionFailure;
-import org.gradle.util.TestFile;
-import org.junit.Test;
-
-import java.io.File;
-
-import static org.hamcrest.Matchers.*;
-
-public class ProjectLoadingIntegrationTest extends AbstractIntegrationTest {
-    @Test
-    public void handlesSimilarlyNamedBuildFilesInSameDirectory() {
-        TestFile buildFile1 = testFile("similarly-named build.gradle").write("task build");
-        TestFile buildFile2 = testFile("similarly_named_build_gradle").write("task 'other-build'");
-
-        usingBuildFile(buildFile1).withTasks("build").run();
-
-        usingBuildFile(buildFile2).withTasks("other-build").run();
-
-        usingBuildFile(buildFile1).withTasks("build").run();
-    }
-
-    @Test
-    public void handlesWhitespaceOnlySettingsAndBuildFiles() {
-        testFile("settings.gradle").write("   \n  ");
-        testFile("build.gradle").write("   ");
-        inTestDirectory().withTaskList().run();
-    }
-
-    @Test
-    public void embeddedBuildFileIgnoresBuildAndScriptFiles() {
-        File rootDir = getTestDir();
-        testFile("settings.gradle").write("throw new RuntimeException()");
-        testFile("build.gradle").write("throw new RuntimeException()");
-        inDirectory(rootDir).usingBuildScript("Task task = task('do-stuff')").withTasks("do-stuff").run();
-    }
-
-    @Test
-    public void canDetermineRootProjectAndDefaultProjectBasedOnCurrentDirectory() {
-        File rootDir = getTestDir();
-        File childDir = new File(rootDir, "child");
-
-        testFile("settings.gradle").write("include('child')");
-        testFile("build.gradle").write("task('do-stuff')");
-        testFile("child/build.gradle").write("task('do-stuff')");
-
-        inDirectory(rootDir).withSearchUpwards().withTasks("do-stuff").run().assertTasksExecuted(":do-stuff", ":child:do-stuff");
-        inDirectory(rootDir).withSearchUpwards().withTasks(":do-stuff").run().assertTasksExecuted(":do-stuff");
-
-        inDirectory(childDir).withSearchUpwards().withTasks("do-stuff").run().assertTasksExecuted(":child:do-stuff");
-        inDirectory(childDir).withSearchUpwards().withTasks(":do-stuff").run().assertTasksExecuted(":do-stuff");
-    }
-
-    @Test
-    public void canDetermineRootProjectAndDefaultProjectBasedOnProjectDirectory() {
-        File rootDir = getTestDir();
-        File childDir = new File(rootDir, "child");
-
-        testFile("settings.gradle").write("include('child')");
-        testFile("build.gradle").write("task('do-stuff')");
-        testFile("child/build.gradle").write("task('do-stuff')");
-
-        usingProjectDir(rootDir).withSearchUpwards().withTasks("do-stuff").run().assertTasksExecuted(":do-stuff", ":child:do-stuff");
-        usingProjectDir(rootDir).withSearchUpwards().withTasks(":do-stuff").run().assertTasksExecuted(":do-stuff");
-
-        usingProjectDir(childDir).withSearchUpwards().withTasks("do-stuff").run().assertTasksExecuted(":child:do-stuff");
-        usingProjectDir(childDir).withSearchUpwards().withTasks(":do-stuff").run().assertTasksExecuted(":do-stuff");
-    }
-
-    @Test
-    public void canDetermineRootProjectAndDefaultProjectBasedOnBuildFile() {
-        testFile("settings.gradle").write("include('child')");
-
-        TestFile rootBuildFile = testFile("build.gradle");
-        rootBuildFile.write("task('do-stuff')");
-
-        TestFile childBuildFile = testFile("child/build.gradle");
-        childBuildFile.write("task('do-stuff')");
-
-        usingBuildFile(rootBuildFile).withSearchUpwards().withTasks("do-stuff").run().assertTasksExecuted(":do-stuff", ":child:do-stuff");
-        usingBuildFile(rootBuildFile).withSearchUpwards().withTasks(":do-stuff").run().assertTasksExecuted(":do-stuff");
-
-        usingBuildFile(childBuildFile).withSearchUpwards().withTasks("do-stuff").run().assertTasksExecuted(":child:do-stuff");
-        usingBuildFile(childBuildFile).withSearchUpwards().withTasks(":do-stuff").run().assertTasksExecuted(":do-stuff");
-    }
-
-    @Test
-    public void buildFailsWhenMultipleProjectsMeetDefaultProjectCriteria() {
-        testFile("settings.gradle").writelns(
-            "include 'child'",
-            "project(':child').projectDir = rootProject.projectDir");
-        testFile("build.gradle").write("// empty");
-
-        ExecutionFailure result = inTestDirectory().withTasks("test").runWithFailure();
-        result.assertThatDescription(startsWith("Could not select the default project for this build. Multiple projects in this build have project directory"));
-
-        result = usingProjectDir(getTestDir()).withTasks("test").runWithFailure();
-        result.assertThatDescription(startsWith("Could not select the default project for this build. Multiple projects in this build have project directory"));
-
-        result = usingBuildFile(testFile("build.gradle")).withTasks("test").runWithFailure();
-        result.assertThatDescription(startsWith("Could not select the default project for this build. Multiple projects in this build have build file"));
-    }
-
-    @Test
-    public void buildFailsWhenSpecifiedBuildFileIsNotAFile() {
-        ExecutionFailure result = usingBuildFile(testFile("unknown build file")).runWithFailure();
-        result.assertThatDescription(startsWith("Build file"));
-        result.assertThatDescription(endsWith("does not exist."));
-    }
-
-    @Test
-    public void buildFailsWhenSpecifiedProjectDirectoryIsNotADirectory() {
-        ExecutionFailure result = usingProjectDir(testFile("unknown dir")).runWithFailure();
-        result.assertThatDescription(startsWith("Project directory"));
-        result.assertThatDescription(endsWith("does not exist."));
-    }
-
-    @Test
-    public void buildFailsWhenSpecifiedSettingsFileIsNotAFile() {
-        ExecutionFailure result = inTestDirectory().usingSettingsFile(testFile("unknown")).runWithFailure();
-        result.assertThatDescription(startsWith("Could not read settings file"));
-        result.assertThatDescription(endsWith("as it does not exist."));
-    }
-
-    @Test
-    public void buildFailsWhenSpecifiedSettingsFileDoesNotContainMatchingProject() {
-        TestFile settingsFile = testFile("settings.gradle");
-        settingsFile.write("// empty");
-
-        TestFile projectdir = testFile("project dir");
-        projectdir.mkdirs();
-
-        ExecutionFailure result = usingProjectDir(projectdir).usingSettingsFile(settingsFile).runWithFailure();
-        result.assertThatDescription(startsWith("Could not select the default project for this build. No projects in this build have project directory"));
-    }
-
-    @Test
-    public void settingsFileTakesPrecedenceOverBuildFileInSameDirectory() {
-        testFile("settings.gradle").write("rootProject.buildFileName = 'root.gradle'");
-        testFile("root.gradle").write("task('do-stuff')");
-        
-        TestFile buildFile = testFile("build.gradle");
-        buildFile.write("throw new RuntimeException()");
-
-        inTestDirectory().withTasks("do-stuff").run();
-        usingProjectDir(getTestDir()).withTasks("do-stuff").run();
-    }
-
-    @Test
-    public void settingsFileInParentDirectoryTakesPrecedenceOverBuildFile() {
-        testFile("settings.gradle").writelns(
-            "include 'child'",
-            "project(':child').buildFileName = 'child.gradle'"
-        );
-
-        TestFile subDirectory = getTestDir().file("child");
-        subDirectory.file("build.gradle").write("throw new RuntimeException()");
-        subDirectory.file("child.gradle").write("task('do-stuff')");
-
-        inDirectory(subDirectory).withSearchUpwards().withTasks("do-stuff").run();
-        usingProjectDir(subDirectory).withSearchUpwards().withTasks("do-stuff").run();
-    }
-
-    @Test
-    public void explicitBuildFileTakesPrecedenceOverSettingsFileInSameDirectory() {
-        testFile("settings.gradle").write("rootProject.buildFileName = 'root.gradle'");
-        testFile("root.gradle").write("throw new RuntimeException()");
-
-        TestFile buildFile = testFile("build.gradle");
-        buildFile.write("task('do-stuff')");
-
-        usingBuildFile(buildFile).withTasks("do-stuff").run();
-    }
-
-    @Test
-    public void ignoresMultiProjectBuildInParentDirectoryWhichDoesNotMeetDefaultProjectCriteria() {
-        testFile("settings.gradle").write("include 'another'");
-        testFile("gradle.properties").writelns("prop=value2", "otherProp=value");
-
-        TestFile subDirectory = getTestDir().file("subdirectory");
-        TestFile buildFile = subDirectory.file("build.gradle");
-        buildFile.writelns("task('do-stuff') << {",
-                "assert prop == 'value'",
-                "assert !project.hasProperty('otherProp')",
-                "}");
-        testFile("subdirectory/gradle.properties").write("prop=value");
-
-        inDirectory(subDirectory).withSearchUpwards().withTasks("do-stuff").run();
-        usingProjectDir(subDirectory).withSearchUpwards().withTasks("do-stuff").run();
-        usingBuildFile(buildFile).withSearchUpwards().withTasks("do-stuff").run();
-    }
-
-    @Test
-    public void multiProjectBuildCanHaveMultipleProjectsWithSameProjectDir() {
-        testFile("settings.gradle").writelns(
-            "include 'child1', 'child2'",
-            "project(':child1').projectDir = new File(settingsDir, 'shared')",
-            "project(':child2').projectDir = new File(settingsDir, 'shared')"
-        );
-        testFile("shared/build.gradle").write("task('do-stuff')");
-
-        inTestDirectory().withTasks("do-stuff").run().assertTasksExecuted(":child1:do-stuff", ":child2:do-stuff");
-    }
-
-    @Test
-    public void multiProjectBuildCanHaveSeveralProjectsWithSameBuildFile() {
-        testFile("settings.gradle").writelns(
-            "include 'child1', 'child2'",
-            "project(':child1').buildFileName = '../child.gradle'",
-            "project(':child2').buildFileName = '../child.gradle'"
-        );
-        testFile("child.gradle").write("task('do-stuff')");
-
-        inTestDirectory().withTasks("do-stuff").run().assertTasksExecuted(":child1:do-stuff", ":child2:do-stuff");
-    }
-
-    @Test
-    public void multiProjectBuildCanHaveSettingsFileAndRootBuildFileInSubDir() {
-        TestFile buildFilesDir = getTestDir().file("root");
-        TestFile settingsFile = buildFilesDir.file("settings.gradle");
-        settingsFile.writelns(
-            "includeFlat 'child'",
-            "rootProject.projectDir = new File(settingsDir, '..')",
-            "rootProject.buildFileName = 'root/build.gradle'"
-        );
-
-        TestFile rootBuildFile = buildFilesDir.file("build.gradle");
-        rootBuildFile.write("task('do-stuff', dependsOn: ':child:task')");
-
-        TestFile childBuildFile = testFile("child/build.gradle");
-        childBuildFile.writelns("task('do-stuff')", "task('task')");
-
-        usingProjectDir(getTestDir()).usingSettingsFile(settingsFile).withTasks("do-stuff").run().assertTasksExecuted(":child:task", ":do-stuff", ":child:do-stuff");
-        usingBuildFile(rootBuildFile).withTasks("do-stuff").run().assertTasksExecuted(":child:task", ":do-stuff", ":child:do-stuff");
-        usingBuildFile(childBuildFile).usingSettingsFile(settingsFile).withTasks("do-stuff").run().assertTasksExecuted(":child:do-stuff");
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesJavaOnlyIfIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesJavaOnlyIfIntegrationTest.groovy
deleted file mode 100644
index 44a3292..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesJavaOnlyIfIntegrationTest.groovy
+++ /dev/null
@@ -1,92 +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.integtests.fixtures.GradleDistribution
-import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.gradle.util.TestFile
-import org.junit.Rule
-import org.junit.Test
-import org.gradle.integtests.fixtures.Sample
-
-public class SamplesJavaOnlyIfIntegrationTest {
-    @Rule public final GradleDistribution dist = new GradleDistribution()
-    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
-    @Rule public final Sample sample = new Sample('java/onlyif')
-
-    /**
-     * runs a build 3 times.
-     * execute clean dists
-     * check worked correctly
-     *
-     * remove test results
-     * execute dists
-     * check didn't re-run tests
-     *
-     * remove class file
-     * execute dists
-     * check that it re-ran tests 
-     */
-    @Test public void testOptimizedBuild() {
-        TestFile javaprojectDir = sample.dir
-
-        // Build and test projects
-        executer.inDirectory(javaprojectDir).withTasks('clean', 'build').run()
-
-        // Check tests have run
-        assertExists(javaprojectDir, 'build/test-results/TEST-org.gradle.PersonTest.xml')
-        assertExists(javaprojectDir, 'build/test-results/TESTS-TestSuites.xml')
-
-        // Check jar exists
-        assertExists(javaprojectDir, "build/libs/onlyif.jar")
-
-        // remove test results
-        removeFile(javaprojectDir, 'build/test-results/TEST-org.gradle.PersonTest.xml')
-        removeFile(javaprojectDir, 'build/test-results/TESTS-TestSuites.xml')
-
-        executer.inDirectory(javaprojectDir).withTasks('test').run()
-
-        // assert that tests did not run
-        // (since neither compile nor compileTests should have done anything)
-        assertDoesNotExist(javaprojectDir, 'build/test-results/TEST-org.gradle.PersonTest.xml')
-        assertDoesNotExist(javaprojectDir, 'build/test-results/TESTS-TestSuites.xml')
-
-        // remove a compiled class file
-        removeFile(javaprojectDir, 'build/classes/main/org/gradle/Person.class')
-
-        executer.inDirectory(javaprojectDir).withTasks('test').run()
-
-        // Check tests have run
-        assertExists(javaprojectDir, 'build/test-results/TEST-org.gradle.PersonTest.xml')
-        assertExists(javaprojectDir, 'build/test-results/TESTS-TestSuites.xml')
-    }
-
-    private static void assertExists(File baseDir, String path) {
-        new TestFile(baseDir).file(path).assertExists()
-    }
-
-    private static void assertDoesNotExist(File baseDir, String path) {
-        new TestFile(baseDir).file(path).assertDoesNotExist()
-    }
-
-    private static void removeFile(File baseDir, String path) {
-        TestFile file = new TestFile(baseDir).file(path)
-        file.assertExists()
-        file.delete()
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesJavaQuickstartIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesJavaQuickstartIntegrationTest.groovy
deleted file mode 100644
index 2740b5a..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesJavaQuickstartIntegrationTest.groovy
+++ /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.integtests
-
-import java.util.jar.Manifest
-import org.gradle.integtests.fixtures.GradleDistribution
-import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.gradle.util.TestFile
-import org.junit.Rule
-import org.junit.Test
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
-import org.gradle.integtests.fixtures.Sample
-
-/**
- * @author Hans Dockter
- */
-class SamplesJavaQuickstartIntegrationTest {
-    @Rule public final GradleDistribution dist = new GradleDistribution()
-    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
-    @Rule public final Sample sample = new Sample('java/quickstart')
-
-    @Test
-    public void canBuildAndUploadJar() {
-        TestFile javaprojectDir = sample.dir
-
-        // Build and test projects
-        executer.inDirectory(javaprojectDir).withTasks('clean', 'build', 'uploadArchives').run()
-
-        // Check tests have run
-        JUnitTestExecutionResult result = new JUnitTestExecutionResult(javaprojectDir)
-        result.assertTestClassesExecuted('org.gradle.PersonTest')
-
-        // Check jar exists
-        javaprojectDir.file("build/libs/quickstart-1.0.jar").assertIsFile()
-
-        // Check jar uploaded
-        javaprojectDir.file('repos/quickstart-1.0.jar').assertIsFile()
-
-        // Check contents of Jar
-        TestFile jarContents = dist.testDir.file('jar')
-        javaprojectDir.file('repos/quickstart-1.0.jar').unzipTo(jarContents)
-        jarContents.assertHasDescendants(
-                'META-INF/MANIFEST.MF',
-                'org/gradle/Person.class'
-        )
-
-        // Check contents of manifest
-        Manifest manifest = new Manifest()
-        jarContents.file('META-INF/MANIFEST.MF').withInputStream { manifest.read(it) }
-        assertThat(manifest.mainAttributes.size(), equalTo(3))
-        assertThat(manifest.mainAttributes.getValue('Manifest-Version'), equalTo('1.0'))
-        assertThat(manifest.mainAttributes.getValue('Implementation-Title'), equalTo('Gradle Quickstart'))
-        assertThat(manifest.mainAttributes.getValue('Implementation-Version'), equalTo('1.0'))
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/ScalaProjectIntegrationTest.java b/subprojects/core/src/integTest/groovy/org/gradle/integtests/ScalaProjectIntegrationTest.java
deleted file mode 100644
index ec5c8a1..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/ScalaProjectIntegrationTest.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.integtests;
-
-import org.junit.Test;
-
-public class ScalaProjectIntegrationTest extends AbstractIntegrationTest {
-    @Test
-    public void handlesEmptyProject() {
-        testFile("build.gradle").writelns(
-                "apply plugin: 'scala'"
-        );
-        inTestDirectory().withTasks("build").run();
-    }
-
-    @Test
-    public void handlesJavaSourceOnly() {
-        testFile("src/main/java/somepackage/SomeClass.java").writelns("public class SomeClass { }");
-        testFile("build.gradle").write("apply plugin: 'scala'");
-        testFile("settings.gradle").write("rootProject.name='javaOnly'");
-        inTestDirectory().withTasks("build").run();
-        testFile("build/libs/javaOnly.jar").assertExists();
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SettingsScriptErrorIntegrationTest.java b/subprojects/core/src/integTest/groovy/org/gradle/integtests/SettingsScriptErrorIntegrationTest.java
deleted file mode 100644
index 1868121..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SettingsScriptErrorIntegrationTest.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.integtests;
-
-import org.gradle.integtests.fixtures.ExecutionFailure;
-import org.gradle.util.TestFile;
-import org.junit.Test;
-
-import java.io.IOException;
-
-public class SettingsScriptErrorIntegrationTest extends AbstractIntegrationTest {
-    @Test
-    public void reportsSettingsScriptEvaluationFailsWithRuntimeException() throws IOException {
-        TestFile buildFile = testFile("some build.gradle");
-        TestFile settingsFile = testFile("some settings.gradle");
-        settingsFile.writelns("", "", "throw new RuntimeException('<failure message>')");
-
-        ExecutionFailure failure = usingBuildFile(buildFile).usingSettingsFile(settingsFile).withTasks("do-stuff")
-                .runWithFailure();
-
-        failure.assertHasFileName(String.format("Settings file '%s'", settingsFile));
-        failure.assertHasLineNumber(3);
-        failure.assertHasDescription("A problem occurred evaluating settings 'reportsSettingsScriptEvaluationFailsWithRuntimeException");
-        failure.assertHasCause("<failure message>");
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SettingsScriptExecutionIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/SettingsScriptExecutionIntegrationTest.groovy
deleted file mode 100644
index 7a43d15..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SettingsScriptExecutionIntegrationTest.groovy
+++ /dev/null
@@ -1,68 +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.integtests.fixtures.ArtifactBuilder
-import org.gradle.integtests.fixtures.ExecutionResult
-import org.gradle.util.TestFile
-import org.junit.Test
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
-
-class SettingsScriptExecutionIntegrationTest extends AbstractIntegrationTest {
-    @Test
-    public void executesSettingsScriptWithCorrectEnvironment() {
-        createExternalJar()
-        createBuildSrc()
-
-        testFile('settings.gradle') << '''
-buildscript {
-    dependencies { classpath files('repo/test-1.3.jar') }
-}
-new org.gradle.test.BuildClass()
-new BuildSrcClass();
-println 'quiet message'
-captureStandardOutput(LogLevel.ERROR)
-println 'error message'
-assert settings != null
-assert buildscript.classLoader == getClass().classLoader.parent
-assert buildscript.classLoader == Thread.currentThread().contextClassLoader
-assert gradle.scriptClassLoader.parent == buildscript.classLoader.parent.parent
-'''
-        testFile('build.gradle') << 'task doStuff'
-
-        ExecutionResult result = inTestDirectory().withTasks('doStuff').run()
-        assertThat(result.output, containsString('quiet message'))
-        assertThat(result.output, not(containsString('error message')))
-        assertThat(result.error, containsString('error message'))
-        assertThat(result.error, not(containsString('quiet message')))
-    }
-
-    private TestFile createBuildSrc() {
-        return testFile('buildSrc/src/main/java/BuildSrcClass.java') << '''
-            public class BuildSrcClass { }
-'''
-    }
-
-    private def createExternalJar() {
-        ArtifactBuilder builder = artifactBuilder();
-        builder.sourceFile('org/gradle/test/BuildClass.java') << '''
-            package org.gradle.test;
-            public class BuildClass { }
-'''
-        builder.buildJar(testFile("repo/test-1.3.jar"))
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SyncTaskIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/SyncTaskIntegrationTest.groovy
deleted file mode 100644
index 06bea7a..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SyncTaskIntegrationTest.groovy
+++ /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.
- */
-package org.gradle.integtests
-
-import org.junit.Test
-
-class SyncTaskIntegrationTest extends AbstractIntegrationTest {
-    @Test
-    public void copiesFilesAndRemovesExtraFilesFromDestDir() {
-        testFile('source').create {
-            dir1 { file 'file1.txt' }
-            dir2 {
-                subdir { file 'file2.txt' }
-                file 'file3.txt'
-            }
-        }
-        testFile('dest').create {
-            file 'extra.txt'
-            extraDir { file 'extra.txt' }
-            dir1 {
-                file 'extra.txt'
-                extraDir { file 'extra.txt' }
-            }
-        }
-
-        testFile('build.gradle') << '''
-            task sync(type: Sync) {
-                into 'dest'
-                from 'source'
-            }
-'''
-
-        inTestDirectory().withTasks('sync').run()
-
-        testFile('dest').assertHasDescendants(
-                'dir1/file1.txt',
-                'dir2/subdir/file2.txt',
-                'dir2/file3.txt'
-        )
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/TaskAutoDependencyIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/TaskAutoDependencyIntegrationTest.groovy
deleted file mode 100644
index 475738d..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/TaskAutoDependencyIntegrationTest.groovy
+++ /dev/null
@@ -1,71 +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.junit.Ignore
-import org.junit.Test
-
-class TaskAutoDependencyIntegrationTest extends AbstractIntegrationTest {
-    @Test
-    public void autoAddsInputFileCollectionAsADependency() {
-        // Include a configuration with transitive dep on a Jar and an unmanaged Jar.
-
-        testFile('settings.gradle') << 'include "a", "b"'
-        testFile('a/build.gradle') << '''
-configurations { compile }
-dependencies { compile project(path: ':b', configuration: 'archives') }
-
-task doStuff(type: InputTask) {
-    src = configurations.compile + fileTree('src/java')
-}
-
-class InputTask extends DefaultTask {
-    @InputFiles
-    def FileCollection src
-}
-'''
-        testFile('b/build.gradle') << '''
-apply plugin: 'base'
-task jar << {
-    file('b.jar').text = 'some jar'
-}
-
-task otherJar(type: Jar) {
-    destinationDir = buildDir
-}
-
-configurations { archives }
-dependencies { archives files('b.jar') { builtBy jar } }
-artifacts { archives otherJar }
-'''
-        inTestDirectory().withTasks('doStuff').run().assertTasksExecuted(':b:jar', ':b:otherJar', ':a:doStuff')
-    }
-
-    @Test @Ignore
-    public void addsDependenciesForInheritedConfiguration() {
-        fail()
-    }
-
-    @Test @Ignore
-    public void addsDependenciesForFileCollectionInSameProject() {
-        fail()
-    }
-    
-    @Test @Ignore
-    public void addsDependenciesForFileCollectionInProjectWithNoArtifacts() {
-        fail()
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/TaskDefinitionIntegrationTest.java b/subprojects/core/src/integTest/groovy/org/gradle/integtests/TaskDefinitionIntegrationTest.java
deleted file mode 100644
index f999825..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/TaskDefinitionIntegrationTest.java
+++ /dev/null
@@ -1,135 +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.junit.Test;
-
-public class TaskDefinitionIntegrationTest extends AbstractIntegrationTest {
-    @Test
-    public void canDefineTasksUsingTaskKeywordAndIdentifier() {
-        testFile("build.gradle").writelns(
-                "task nothing",
-                "task withAction << { }",
-                "task emptyOptions()",
-                "task task",
-                "task withOptions(dependsOn: [nothing, withAction, emptyOptions, task])",
-                "task withOptionsAndAction(dependsOn: withOptions) << { }");
-        inTestDirectory().withTasks("withOptionsAndAction").run().assertTasksExecuted(":emptyOptions", ":nothing",
-                ":task", ":withAction", ":withOptions", ":withOptionsAndAction");
-    }
-
-    @Test
-    public void canDefineTasksUsingTaskKeywordAndGString() {
-        testFile("build.gradle").writelns(
-                "v = 'Task'",
-                "task \"nothing$v\"",
-                "task \"withAction$v\" << { }",
-                "task \"emptyOptions$v\"()",
-                "task \"withOptions$v\"(dependsOn: [nothingTask, withActionTask, emptyOptionsTask])",
-                "task \"withOptionsAndAction$v\"(dependsOn: withOptionsTask) << { }");
-        inTestDirectory().withTasks("withOptionsAndActionTask").run().assertTasksExecuted(":emptyOptionsTask",
-                ":nothingTask", ":withActionTask", ":withOptionsTask", ":withOptionsAndActionTask");
-    }
-
-    @Test
-    public void canDefineTasksUsingTaskKeywordAndString() {
-        testFile("build.gradle").writelns(
-                "task 'nothing'",
-                "task 'withAction' << { }",
-                "task 'emptyOptions'()",
-                "task 'withOptions'(dependsOn: [nothing, withAction, emptyOptions])",
-                "task 'withOptionsAndAction'(dependsOn: withOptions) << { }");
-        inTestDirectory().withTasks("withOptionsAndAction").run().assertTasksExecuted(":emptyOptions", ":nothing",
-                ":withAction", ":withOptions", ":withOptionsAndAction");
-    }
-
-    @Test
-    public void canDefineTasksInNestedBlocks() {
-        testFile("build.gradle").writelns(
-                "2.times { task \"dynamic$it\" << { } }",
-                "if (dynamic0) { task inBlock }",
-                "def task() { task inMethod }",
-                "task()", "def cl = { -> task inClosure }",
-                "cl()",
-                "task all(dependsOn: [dynamic0, dynamic1, inBlock, inMethod, inClosure])");
-        inTestDirectory().withTasks("all").run().assertTasksExecuted(":dynamic0", ":dynamic1", ":inBlock", ":inClosure",
-                ":inMethod", ":all");
-    }
-
-    @Test
-    public void canDefineTasksUsingTaskMethodExpression() {
-        testFile("build.gradle").writelns(
-                "a = 'a' == 'b' ? null: task(withAction) << { }",
-                "a = task(nothing)",
-                "a = task(emptyOptions())", "taskName = 'dynamic'",
-                "a = task(\"$taskName\") << { }",
-                "a = task('string')",
-                "a = task('stringWithAction') << { }",
-                "a = task('stringWithOptions', description: 'description')",
-                "a = task('stringWithOptionsAndAction', description: 'description') << { }",
-                "a = task(withOptions, description: 'description')",
-                "a = task(withOptionsAndAction, description: 'description') << { }",
-                "a = task(anotherWithAction).doFirst\n{}", "task all(dependsOn: tasks.all)");
-        inTestDirectory().withTasks("all").run().assertTasksExecuted(":anotherWithAction", ":dynamic", ":emptyOptions",
-                ":nothing", ":string", ":stringWithAction", ":stringWithOptions", ":stringWithOptionsAndAction",
-                ":withAction", ":withOptions", ":withOptionsAndAction", ":all");
-    }
-
-    @Test
-    public void canConfigureTasksWhenTheyAreDefined() {
-        testFile("build.gradle").writelns(
-                "task withDescription { description = 'value' }",
-                "task(asMethod)\n{ description = 'value' }",
-                "task asStatement(type: TestTask) { property = 'value' }",
-                "task \"dynamic\"(type: TestTask) { property = 'value' }",
-                "v = task(asExpression, type: TestTask) { property = 'value' }",
-                "task(postConfigure, type: TestTask).configure { property = 'value' }",
-                "[asStatement, dynamic, asExpression, postConfigure].each { ",
-                "    assert 'value' == it.property",
-                "}",
-                "[withDescription, asMethod].each {",
-                "    assert 'value' == it.description",
-                "}",
-                "task all(dependsOn: tasks.all)",
-                "class TestTask extends DefaultTask { String property }");
-        inTestDirectory().withTasks("all").run();
-    }
-
-    @Test
-    public void doesNotHideLocalMethodsAndVariables() {
-        testFile("build.gradle").writelns(
-                "String name = 'a'; task name",
-//                "taskNameVar = 'b'; task taskNameVar",
-                "def taskNameMethod(String name = 'c') { name } ",
-//                "task taskNameMethod",
-                "task taskNameMethod('d')",
-                "def addTaskMethod(String methodParam) { task methodParam }",
-                "addTaskMethod('e')",
-                "def addTaskWithClosure(String methodParam) { task(methodParam) { property = 'value' } }",
-                "addTaskWithClosure('f')",
-                "def addTaskWithMap(String methodParam) { task(methodParam, description: 'description') }",
-                "addTaskWithMap('g')",
-                "cl = { String taskNameParam -> task taskNameParam }",
-                "cl.call('h')",
-                "cl = { String taskNameParam -> task(taskNameParam) { property = 'value' } }",
-                "cl.call('i')",
-                "assert 'value' == f.property",
-                "assert 'value' == i.property",
-                "task all(dependsOn: tasks.all)");
-        inTestDirectory().withTasks("all").run().assertTasksExecuted(":a", ":d", ":e", ":f", ":g", ":h", ":i", ":all");
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/TaskExecutionIntegrationTest.java b/subprojects/core/src/integTest/groovy/org/gradle/integtests/TaskExecutionIntegrationTest.java
deleted file mode 100644
index 4e63cd8..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/TaskExecutionIntegrationTest.java
+++ /dev/null
@@ -1,153 +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.util.TestFile;
-import org.junit.Test;
-
-import static org.hamcrest.Matchers.*;
-
-public class TaskExecutionIntegrationTest extends AbstractIntegrationTest {
-    @Test
-    public void taskCanAccessTaskGraph() {
-        TestFile buildFile = testFile("build.gradle");
-        buildFile.writelns(
-                "boolean notified = false",
-                "task a(dependsOn: 'b') << { task ->",
-                "    assert notified",
-                "    assert gradle.taskGraph.hasTask(task)",
-                "    assert gradle.taskGraph.hasTask(':a')",
-                "    assert gradle.taskGraph.hasTask(a)",
-                "    assert gradle.taskGraph.hasTask(':b')",
-                "    assert gradle.taskGraph.hasTask(b)",
-                "    assert gradle.taskGraph.allTasks.contains(task)",
-                "    assert gradle.taskGraph.allTasks.contains(tasks.getByName('b'))",
-                "}",
-                "task b",
-                "gradle.taskGraph.whenReady { graph ->",
-                "    assert graph.hasTask(':a')",
-                "    assert graph.hasTask(a)",
-                "    assert graph.hasTask(':b')",
-                "    assert graph.hasTask(b)",
-                "    assert graph.allTasks.contains(a)",
-                "    assert graph.allTasks.contains(b)",
-                "    notified = true",
-                "}");
-        usingBuildFile(buildFile).withTasks("a").run().assertTasksExecuted(":b", ":a");
-    }
-
-    @Test
-    public void executesAllTasksInASingleBuildAndEachTaskAtMostOnce() {
-        TestFile buildFile = testFile("build.gradle");
-        buildFile.writelns(
-                "gradle.taskGraph.whenReady { assert !project.hasProperty('graphReady'); graphReady = true }",
-                "task a << { task -> project.executedA = task }",
-                "task b << { ",
-                "    assert a == project.executedA",
-                "    assert gradle.taskGraph.hasTask(':a')",
-                "}",
-                "task c(dependsOn: a)",
-                "task d(dependsOn: a)",
-                "task e(dependsOn: [a, d])");
-        usingBuildFile(buildFile).withTasks("a", "b").run().assertTasksExecuted(":a", ":b");
-        usingBuildFile(buildFile).withTasks("a", "a").run().assertTasksExecuted(":a");
-        usingBuildFile(buildFile).withTasks("c", "a").run().assertTasksExecuted(":a", ":c");
-        usingBuildFile(buildFile).withTasks("c", "e").run().assertTasksExecuted(":a", ":c", ":d", ":e");
-    }
-
-    @Test
-    public void executesMultiProjectsTasksInASingleBuildAndEachTaskAtMostOnce() {
-        testFile("settings.gradle").writelns("include 'child1', 'child2'");
-        TestFile buildFile = testFile("build.gradle");
-        buildFile.writelns(
-                "task a",
-                "allprojects {",
-                "    task b",
-                "    task c(dependsOn: ['b', ':a'])",
-                "}");
-        usingBuildFile(buildFile).withTasks("a", "c").run().assertTasksExecuted(":a", ":b", ":c", ":child1:b",
-                ":child1:c", ":child2:b", ":child2:c");
-        usingBuildFile(buildFile).withTasks("b", ":child2:c").run().assertTasksExecuted(":b", ":child1:b", ":child2:b",
-                ":a", ":child2:c");
-    }
-
-    @Test
-    public void executesMultiProjectDefaultTasksInASingleBuildAndEachTaskAtMostOnce() {
-        testFile("settings.gradle").writelns("include 'child1', 'child2'");
-        TestFile buildFile = testFile("build.gradle");
-        buildFile.writelns("defaultTasks 'a', 'b'", "task a", "subprojects {", "    task a(dependsOn: ':a')",
-                "    task b(dependsOn: ':a')", "}");
-        usingBuildFile(buildFile).run().assertTasksExecuted(":a", ":child1:a", ":child2:a", ":child1:b", ":child2:b");
-    }
-
-    @Test
-    public void executesProjectDefaultTasksWhenNoneSpecified() {
-        TestFile buildFile = testFile("build.gradle");
-        buildFile.writelns(
-                "task a",
-                "task b(dependsOn: a)",
-                "defaultTasks 'b'"
-        );
-        usingBuildFile(buildFile).withTasks().run().assertTasksExecuted(":a", ":b");
-    }
-    
-    @Test
-    public void doesNotExecuteTaskActionsWhenDryRunSpecified() {
-        TestFile buildFile = testFile("build.gradle");
-        buildFile.writelns(
-                "task a << { fail() }",
-                "task b(dependsOn: a) << { fail() }",
-                "defaultTasks 'b'"
-        );
-
-        // project defaults
-        usingBuildFile(buildFile).withArguments("-m").run().assertTasksExecuted(":a", ":b");
-        // named tasks
-        usingBuildFile(buildFile).withArguments("-m").withTasks("b").run().assertTasksExecuted(":a", ":b");
-    }
-
-    @Test
-    public void excludesTasksWhenExcludePatternSpecified() {
-        testFile("settings.gradle").write("include 'sub'");
-        TestFile buildFile = testFile("build.gradle");
-        buildFile.writelns(
-                "task a",
-                "task b(dependsOn: a)",
-                "task c(dependsOn: [a, b])",
-                "task d(dependsOn: c)",
-                "defaultTasks 'd'"
-        );
-        testFile("sub/build.gradle").writelns(
-                "task c",
-                "task d(dependsOn: c)"
-        );
-
-        // Exclude entire branch
-        usingBuildFile(buildFile).withTasks(":d").withArguments("-x", "c").run().assertTasksExecuted(":d");
-        // Exclude direct dependency
-        usingBuildFile(buildFile).withTasks(":d").withArguments("-x", "b").run().assertTasksExecuted(":a", ":c", ":d");
-        // Exclude using paths and multi-project
-        usingBuildFile(buildFile).withTasks("d").withArguments("-x", "c").run().assertTasksExecuted(":d", ":sub:d");
-        usingBuildFile(buildFile).withTasks("d").withArguments("-x", "sub:c").run().assertTasksExecuted(":a", ":b", ":c", ":d", ":sub:d");
-        usingBuildFile(buildFile).withTasks("d").withArguments("-x", ":sub:c").run().assertTasksExecuted(":a", ":b", ":c", ":d", ":sub:d");
-        usingBuildFile(buildFile).withTasks("d").withArguments("-x", "d").run().assertTasksExecuted();
-        // Project defaults
-        usingBuildFile(buildFile).withArguments("-x", "b").run().assertTasksExecuted(":a", ":c", ":d", ":sub:c", ":sub:d");
-        // Unknown task
-        usingBuildFile(buildFile).withTasks("d").withArguments("-x", "unknown").runWithFailure().assertThatDescription(startsWith("Task 'unknown' not found in root project"));
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/UserGuideSamplesIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/UserGuideSamplesIntegrationTest.groovy
deleted file mode 100644
index 9b5b764..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/UserGuideSamplesIntegrationTest.groovy
+++ /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.integtests
-
-import org.junit.runner.RunWith
-
- at RunWith(UserGuideSamplesRunner.class)
-class UserGuideSamplesIntegrationTest {
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/UserGuideSamplesRunner.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/UserGuideSamplesRunner.groovy
deleted file mode 100644
index cd1e8d9..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/UserGuideSamplesRunner.groovy
+++ /dev/null
@@ -1,261 +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 groovy.io.PlatformLineWriter
-import junit.framework.AssertionFailedError
-import org.apache.tools.ant.taskdefs.Delete
-import org.gradle.integtests.fixtures.ExecutionResult
-import org.gradle.integtests.fixtures.GradleDistribution
-import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.gradle.util.AntUtil
-import org.junit.Assert
-import org.junit.runner.Description
-import org.junit.runner.Runner
-import org.junit.runner.notification.Failure
-import org.junit.runner.notification.RunNotifier
-import com.google.common.collect.ListMultimap
-import com.google.common.collect.ArrayListMultimap
-
-class UserGuideSamplesRunner extends Runner {
-    static final String NL = System.properties['line.separator']
-    Class<?> testClass
-    Description description
-    Map<Description, SampleRun> samples;
-    GradleDistribution dist = new GradleDistribution()
-    GradleDistributionExecuter executer = new GradleDistributionExecuter(dist)
-
-    def UserGuideSamplesRunner(Class<?> testClass) {
-        this.testClass = testClass
-        this.description = Description.createSuiteDescription(testClass)
-        samples = new LinkedHashMap()
-        for (sample in getScriptsForSamples(dist.userGuideInfoDir)) {
-            Description childDescription = Description.createTestDescription(testClass, sample.id)
-            description.addChild(childDescription)
-            samples.put(childDescription, sample)
-
-            println "Sample $sample.id dir: $sample.subDir"
-            sample.runs.each { println "    args: $it.args expect: $it.outputFile" }
-        }
-    }
-
-    Description getDescription() {
-        return description
-    }
-
-    void run(RunNotifier notifier) {
-        for (childDescription in description.children) {
-            notifier.fireTestStarted(childDescription)
-            SampleRun sampleRun = samples.get(childDescription)
-            try {
-                cleanup(sampleRun)
-                for (run in sampleRun.runs) {
-                    runSample(run)
-                }
-            } catch (Throwable t) {
-                notifier.fireTestFailure(new Failure(childDescription, t))
-            }
-            notifier.fireTestFinished(childDescription)
-        }
-    }
-
-    private def cleanup(SampleRun run) {
-        // Clean up previous runs
-        File rootProjectDir = dist.samplesDir.file(run.subDir)
-        if (rootProjectDir.exists()) {
-            def delete = new Delete()
-            delete.dir = rootProjectDir
-            delete.includes = "**/.gradle/** **/build/**"
-            AntUtil.execute(delete)
-        }
-    }
-
-    private def runSample(GradleRun run) {
-        try {
-            println("Test Id: $run.id, dir: $run.subDir, args: $run.args")
-            File rootProjectDir = dist.samplesDir.file(run.subDir)
-            executer.inDirectory(rootProjectDir).withArguments(run.args as String[]).withEnvironmentVars(run.envs)
-
-            ExecutionResult result = run.expectFailure ? executer.runWithFailure() : executer.run()
-            if (run.outputFile) {
-                String expectedResult = replaceWithPlatformNewLines(dist.userGuideOutputDir.file(run.outputFile).text)
-                try {
-                    compareStrings(expectedResult, result.output, run.ignoreExtraLines)
-                } catch (AssertionFailedError e) {
-                    println 'Expected Result:'
-                    println expectedResult
-                    println 'Actual Result:'
-                    println result.output
-                    println '---'
-                    throw e
-                }
-            }
-
-            run.files.each { path ->
-                println "  checking file '$path' exists"
-                File file = new File(rootProjectDir, path).canonicalFile
-                Assert.assertTrue("Expected file '$file' does not exist.", file.exists())
-                Assert.assertTrue("Expected file '$file' is not a file.", file.isFile())
-            }
-            run.dirs.each { path ->
-                println "  checking directory '$path' exists"
-                File file = new File(rootProjectDir, path).canonicalFile
-                Assert.assertTrue("Expected directory '$file' does not exist.", file.exists())
-                Assert.assertTrue("Expected directory '$file' is not a directory.", file.isDirectory())
-            }
-        } catch (Throwable e) {
-            throw new AssertionError("Integration test for sample '$run.id' in dir '$run.subDir' with args $run.args failed:${NL}$e.message").initCause(e)
-        }
-    }
-
-    private def compareStrings(String expected, String actual, boolean ignoreExtraLines) {
-        List actualLines = normaliseOutput(actual.readLines())
-        List expectedLines = expected.readLines()
-        int pos = 0
-        for (; pos < actualLines.size() && pos < expectedLines.size(); pos++) {
-            String expectedLine = expectedLines[pos]
-            String actualLine = actualLines[pos]
-            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}---")
-                }
-                if (actualLine.contains(expectedLine)) {
-                    Assert.fail("Extra text at line ${pos + 1}.${NL}Expected: ${expectedLine}${NL}Actual: ${actualLine}${NL}---${NL}Actual output:${NL}$actual${NL}---")
-                }
-                Assert.fail("Unexpected value at line ${pos + 1}.${NL}Expected: ${expectedLine}${NL}Actual: ${actualLine}${NL}---${NL}Actual output:${NL}$actual${NL}---")
-            }
-        }
-        if (pos == actualLines.size() && pos < expectedLines.size()) {
-            Assert.fail("Lines missing from actual result, starting at line ${pos + 1}.${NL}Expected: ${expectedLines[pos]}${NL}Actual output:${NL}$actual${NL}---")
-        }
-        if (!ignoreExtraLines && pos < actualLines.size() && pos == expectedLines.size()) {
-            Assert.fail("Extra lines in actual result, starting at line ${pos + 1}.${NL}Actual: ${actualLines[pos]}${NL}Actual output:${NL}$actual${NL}---")
-        }
-    }
-
-    static String replaceWithPlatformNewLines(String text) {
-        StringWriter stringWriter = new StringWriter()
-        new PlatformLineWriter(stringWriter).withWriter { it.write(text) }
-        stringWriter.toString()
-    }
-
-    List<String> normaliseOutput(List<String> lines) {
-        lines.inject(new ArrayList<String>()) { List values, String line ->
-            if (line.matches('Download .+')) {
-                // ignore
-            } else {
-                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 Collection<SampleRun> getScriptsForSamples(File userguideInfoDir) {
-        Node samples = new XmlParser().parse(new File(userguideInfoDir, 'samples.xml'))
-        ListMultimap<String, GradleRun> samplesByDir = ArrayListMultimap.create()
-
-        samples.children().each {Node sample ->
-            String id = sample.'@id'
-            String dir = sample.'@dir'
-            String args = sample.'@args'
-            String outputFile = sample.'@outputFile'
-            boolean ignoreExtraLines = Boolean.valueOf(sample.'@ignoreExtraLines')
-
-            GradleRun run = new GradleRun(id: id)
-            run.subDir = dir
-            run.args = args ? args.split('\\s+') as List : []
-            run.outputFile = outputFile
-            run.ignoreExtraLines = ignoreExtraLines as boolean
-
-            sample.file.each { file -> run.files << file.'@path' }
-            sample.dir.each { file -> run.dirs << file.'@path' }
-
-            samplesByDir.put(dir, run)
-        }
-
-        // Some custom values
-        samplesByDir.get('userguide/tutorial/properties').each { it.envs['ORG_GRADLE_PROJECT_envProjectProp'] = 'envPropertyValue' }
-        samplesByDir.get('userguide/buildlifecycle/taskExecutionEvents')*.expectFailure = true
-        samplesByDir.get('userguide/buildlifecycle/buildProjectEvaluateEvents')*.expectFailure = true
-
-        Map<String, SampleRun> samplesById = new TreeMap<String, SampleRun>()
-
-        // Remove duplicates for a given directory.
-        samplesByDir.asMap().values().collect {List<GradleRun> dirSamples ->
-            Collection<GradleRun> runs = dirSamples.findAll {it.mustRun}
-            if (!runs) {
-                // No samples in this dir have any args, so just run gradle tasks in the dir
-                def sample = dirSamples[0]
-                sample.args = ['tasks']
-                sample
-            } else {
-                return runs
-            }
-        }.flatten().each { GradleRun run ->
-            // Collect up into sample runs
-            SampleRun sampleRun = samplesById[run.id]
-            if (!sampleRun) {
-                sampleRun = new SampleRun(id: run.id, subDir: run.subDir)
-                samplesById[run.id] = sampleRun
-            }
-            sampleRun.runs << run
-        }
-
-        return samplesById.values()
-    }
-}
-
-class SampleRun {
-    String id
-    String subDir
-    List<GradleRun> runs = []
-}
-
-class GradleRun {
-    String id
-    List args = []
-    String subDir
-    Map envs = [:]
-    String outputFile
-    boolean expectFailure
-    boolean ignoreExtraLines
-    List files = []
-    List dirs = []
-
-    boolean getMustRun() {
-        return args || files || dirs
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/WaterProjectIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/WaterProjectIntegrationTest.groovy
deleted file mode 100644
index a404780..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/WaterProjectIntegrationTest.groovy
+++ /dev/null
@@ -1,77 +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.integtests
-
-import org.gradle.integtests.fixtures.ExecutionResult
-import org.gradle.integtests.fixtures.GradleDistribution
-import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.junit.Rule
-import org.junit.Test
-import static org.junit.Assert.*
-import org.gradle.integtests.fixtures.Sample
-
-/**
- * @author Hans Dockter
- */
-class WaterProjectIntegrationTest {
-    final static String NL = System.properties['line.separator']
-
-    final static String HELLO_CLAUSE = "Hello, I'm "
-    final static String CHILDREN_TEXT = 'I love water.'
-    final static String WATER_INFO = 'As you all know, I cover three quarters of this planet!'
-    final static String BLUE_WHALE_INFO = "I'm the largets animal which has ever lived on this planet!"
-    final static String KRILL_INFO = "The weight of my species in summer is twice as heavy as all human beings!"
-    final static String PHYTOPLANKTON_INFO = "I produce as much oxygen as all the other plants on earth together!"
-
-    final static String WATER_NAME = 'water'
-    final static String BLUE_WHALE_NAME = 'bluewhale'
-    final static String KRILL_NAME = 'krill'
-    final static String PHYTOPLANKTON_NAME = 'phytoplankton'
-
-    @Rule public final GradleDistribution dist = new GradleDistribution()
-    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
-    @Rule public final Sample sample = new Sample(WATER_NAME)
-
-    @Test
-    public void waterProject() {
-        File waterDir = sample.dir
-        ExecutionResult result = executer.inDirectory(waterDir).withTasks('hello').withQuietLogging().run()
-        assertEquals(result.output, list2text([intro(WATER_NAME), WATER_INFO,
-                intro(PHYTOPLANKTON_NAME), CHILDREN_TEXT, PHYTOPLANKTON_INFO,
-                intro(KRILL_NAME), CHILDREN_TEXT, KRILL_INFO,
-                intro(BLUE_WHALE_NAME), CHILDREN_TEXT, BLUE_WHALE_INFO]))
-
-        result = executer.inDirectory(new File(waterDir, BLUE_WHALE_NAME)).withTasks('hello').withQuietLogging().run()
-        assertEquals(result.output, list2text([intro(WATER_NAME), WATER_INFO,
-                intro(PHYTOPLANKTON_NAME), CHILDREN_TEXT, PHYTOPLANKTON_INFO,
-                intro(KRILL_NAME), CHILDREN_TEXT, KRILL_INFO,
-                intro(BLUE_WHALE_NAME), CHILDREN_TEXT, BLUE_WHALE_INFO]))
-
-        result = executer.inDirectory(new File(waterDir, PHYTOPLANKTON_NAME)).withTasks('hello').withQuietLogging().run()
-        assertEquals(result.output, list2text([intro(WATER_NAME), WATER_INFO,
-                intro(PHYTOPLANKTON_NAME), CHILDREN_TEXT, PHYTOPLANKTON_INFO]))
-    }
-
-    static String intro(String projectName) {
-        HELLO_CLAUSE + projectName
-    }
-
-    static String list2text(List list) {
-        list.join(NL) + NL
-    }
-
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/WebProjectIntegrationTest.java b/subprojects/core/src/integTest/groovy/org/gradle/integtests/WebProjectIntegrationTest.java
deleted file mode 100644
index ede24e1..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/WebProjectIntegrationTest.java
+++ /dev/null
@@ -1,80 +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.util.TestFile;
-import org.junit.Test;
-
-public class WebProjectIntegrationTest extends AbstractIntegrationTest {
-    @Test
-    public void handlesEmptyProject() {
-        TestFile buildFile = testFile("build.gradle");
-        buildFile.writelns(
-                "apply plugin: 'war'"
-        );
-
-        usingBuildFile(buildFile).withTasks("build").run();
-    }
-
-    @Test
-    public void createsAWar() {
-        testFile("settings.gradle").writelns("rootProject.name = 'test'");
-        TestFile buildFile = testFile("build.gradle");
-        buildFile.writelns(
-                "apply plugin: 'war'"
-        );
-        testFile("src/main/webapp/index.jsp").write("<p>hi</p>");
-
-        usingBuildFile(buildFile).withTasks("assemble").run();
-        testFile("build/libs/test.war").assertIsFile();
-    }
-
-    @Test
-    public void canCustomiseArchiveNamesUsingConventionProperties() {
-        testFile("settings.gradle").writelns("rootProject.name = 'test'");
-        TestFile buildFile = testFile("build.gradle");
-        buildFile.writelns(
-                "apply plugin: 'war'",
-                "jar.enabled = true",
-                "buildDirName = 'output'",
-                "libsDirName = 'archives'",
-                "archivesBaseName = 'test'",
-                "version = '0.5-RC2'"
-        );
-        testFile("src/main/resources/org/gradle/resource.file").write("some resource");
-
-        usingBuildFile(buildFile).withTasks("assemble").run();
-        testFile("output/archives/test-0.5-RC2.jar").assertIsFile();
-        testFile("output/archives/test-0.5-RC2.war").assertIsFile();
-    }
-
-    @Test
-    public void generatesArtifactsWhenVersionIsEmpty() {
-        testFile("settings.gradle").write("rootProject.name = 'empty'");
-        TestFile buildFile = testFile("build.gradle");
-        buildFile.writelns(
-                "apply plugin: 'war'",
-                "jar.enabled = true",
-                "version = ''"
-        );
-        testFile("src/main/resources/org/gradle/resource.file").write("some resource");
-
-        usingBuildFile(buildFile).withTasks("assemble").run();
-        testFile("build/libs/empty.jar").assertIsFile();
-        testFile("build/libs/empty.war").assertIsFile();
-    }
-
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/WrapperProjectIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/WrapperProjectIntegrationTest.groovy
deleted file mode 100644
index 8d5c838..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/WrapperProjectIntegrationTest.groovy
+++ /dev/null
@@ -1,45 +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.integtests
-
-import org.gradle.integtests.fixtures.ExecutionResult
-import org.gradle.integtests.fixtures.GradleDistribution
-import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.junit.Rule
-import org.junit.Test
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
-import org.gradle.integtests.fixtures.Sample
-
-/**
- * @author Hans Dockter
- */
-class WrapperProjectIntegrationTest {
-    @Rule public final GradleDistribution dist = new GradleDistribution()
-    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
-    @Rule public final Sample sample = new Sample('wrapper-project')
-
-    @Test
-    public void wrapperSample() {
-        File wrapperSampleDir = sample.dir
-
-        executer.inDirectory(wrapperSampleDir).withTasks('wrapper').run()
-
-        ExecutionResult result = executer.usingExecutable('gradlew').inDirectory(wrapperSampleDir).withTasks('hello').run()
-        assertThat(result.output, containsString('hello'))
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/DaemonGradleExecuter.java b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/DaemonGradleExecuter.java
index ae43922..004867b 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/DaemonGradleExecuter.java
+++ b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/DaemonGradleExecuter.java
@@ -29,7 +29,7 @@ public class DaemonGradleExecuter extends ForkingGradleExecuter {
 
     @Override
     protected Map doRun(boolean expectFailure) {
-        addShutdownHook(getUserHomeDir());
+        registerDaemon(getUserHomeDir());
         Map result = super.doRun(expectFailure);
         String output = (String) result.get("output");
         output = output.replace(String.format("Note: the Gradle build daemon is an experimental feature.%n"), "");
@@ -38,13 +38,19 @@ public class DaemonGradleExecuter extends ForkingGradleExecuter {
         return result;
     }
 
-    private void addShutdownHook(final File userHomeDir) {
+    public static void registerDaemon(final File userHomeDir) {
+        assert userHomeDir != null;
         if (!DAEMONS.add(userHomeDir)) {
             return;
         }
         Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
             public void run() {
-                new ForkingGradleExecuter(getGradleHomeDir()).withUserHomeDir(userHomeDir).withArguments("--stop").run();
+//                ExecHandleBuilder builder = new ExecHandleBuilder();
+//                builder.workingDir(new File(".").getAbsolutePath());
+//                builder.executable(Jvm.current().getJpsExecutable());
+//                builder.args("-lm");
+//                builder.setStandardOutput(new ByteArrayOutputStream());
+//                builder.build().start().waitForFinish();
             }
         }));
     }
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/ExecutionFailure.java b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/ExecutionFailure.java
index a6ebc8d..01bd531 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/ExecutionFailure.java
+++ b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/ExecutionFailure.java
@@ -29,4 +29,6 @@ public interface ExecutionFailure extends ExecutionResult {
     ExecutionFailure assertHasDescription(String context);
 
     ExecutionFailure assertThatDescription(Matcher<String> matcher);
+
+    ExecutionFailure assertHasNoCause();
 }
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/ExecutionResult.java b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/ExecutionResult.java
index 8b1a8c4..d28d51a 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/ExecutionResult.java
+++ b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/ExecutionResult.java
@@ -35,7 +35,17 @@ public interface ExecutionResult {
     ExecutionResult assertTasksSkipped(String... taskPaths);
 
     /**
+     * Asserts the given task has been skipped. Note: ignores buildSrc tasks.
+     */
+    ExecutionResult assertTaskSkipped(String taskPath);
+
+    /**
      * Asserts that exactly the given set of tasks have not been skipped. Note: ignores buildSrc tasks.
      */
     ExecutionResult assertTasksNotSkipped(String... taskPaths);
+
+    /**
+     * Asserts that the given task has not been skipped. Note: ignores buildSrc tasks.
+     */
+    ExecutionResult assertTaskNotSkipped(String taskPath);
 }
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/ForkingGradleExecuter.java b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/ForkingGradleExecuter.java
index c8669e3..21e0c41 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/ForkingGradleExecuter.java
+++ b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/ForkingGradleExecuter.java
@@ -31,16 +31,20 @@ import java.io.*;
 import java.util.*;
 import java.util.regex.Pattern;
 
-import static org.gradle.util.Matchers.*;
+import static org.gradle.util.Matchers.containsLine;
+import static org.gradle.util.Matchers.matchesRegexp;
 import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
 
 public class ForkingGradleExecuter extends AbstractGradleExecuter {
     private static final Logger LOG = LoggerFactory.getLogger(ForkingGradleExecuter.class);
     private final TestFile gradleHomeDir;
+    private final List<String> gradleOpts = new ArrayList<String>();
 
     public ForkingGradleExecuter(TestFile gradleHomeDir) {
         this.gradleHomeDir = gradleHomeDir;
+        gradleOpts.add("-ea");
     }
 
     public TestFile getGradleHomeDir() {
@@ -59,6 +63,13 @@ public class ForkingGradleExecuter extends AbstractGradleExecuter {
         return new ForkedExecutionFailure(result);
     }
 
+    /**
+     * Adds some options to the GRADLE_OPTS environment variable to use.
+     */
+    public void addGradleOpts(String... opts) {
+        gradleOpts.addAll(Arrays.asList(opts));
+    }
+
     protected Map doRun(boolean expectFailure) {
         gradleHomeDir.assertIsDir();
 
@@ -80,7 +91,7 @@ public class ForkingGradleExecuter extends AbstractGradleExecuter {
         builder.setErrorOutput(errStream);
         builder.environment("GRADLE_HOME", "");
         builder.environment("JAVA_HOME", Jvm.current().getJavaHome());
-        builder.environment("GRADLE_OPTS", "-ea");
+        builder.environment("GRADLE_OPTS", GUtil.join(gradleOpts, " "));
         builder.environment(getEnvironmentVars());
         builder.workingDir(getWorkingDir());
 
@@ -173,6 +184,12 @@ public class ForkingGradleExecuter extends AbstractGradleExecuter {
             return this;
         }
 
+        public ExecutionResult assertTaskSkipped(String taskPath) {
+            Set<String> tasks = new HashSet<String>(grepTasks(skippedTaskPattern));
+            assertThat(String.format("Expected skipped task %s not found in process output:%n%s", taskPath, getOutput()), tasks, hasItem(taskPath));
+            return this;
+        }
+
         public ExecutionResult assertTasksNotSkipped(String... taskPaths) {
             Set<String> tasks = new HashSet<String>(grepTasks(notSkippedTaskPattern));
             Set<String> expectedTasks = new HashSet<String>(Arrays.asList(taskPaths));
@@ -180,6 +197,12 @@ public class ForkingGradleExecuter extends AbstractGradleExecuter {
             return this;
         }
 
+        public ExecutionResult assertTaskNotSkipped(String taskPath) {
+            Set<String> tasks = new HashSet<String>(grepTasks(notSkippedTaskPattern));
+            assertThat(String.format("Expected executed task %s not found in process output:%n%s", taskPath, getOutput()), tasks, hasItem(taskPath));
+            return this;
+        }
+
         private List<String> grepTasks(final Pattern pattern) {
             final List<String> tasks = new ArrayList<String>();
 
@@ -212,6 +235,8 @@ public class ForkingGradleExecuter extends AbstractGradleExecuter {
     }
 
     private static class ForkedExecutionFailure extends ForkedExecutionResult implements ExecutionFailure {
+        private final Pattern causePattern = Pattern.compile("(?m)^Cause: ");
+
         public ForkedExecutionFailure(Map result) {
             super(result);
         }
@@ -232,7 +257,6 @@ public class ForkingGradleExecuter extends AbstractGradleExecuter {
         }
 
         public ExecutionFailure assertThatCause(Matcher<String> matcher) {
-            Pattern causePattern = Pattern.compile("(?m)^Cause: ");
             String error = getError();
             java.util.regex.Matcher regExpMatcher = causePattern.matcher(error);
             int pos = 0;
@@ -257,6 +281,11 @@ public class ForkingGradleExecuter extends AbstractGradleExecuter {
             return this;
         }
 
+        public ExecutionFailure assertHasNoCause() {
+            assertThat(getError(), not(matchesRegexp(causePattern)));
+            return this;
+        }
+
         public ExecutionFailure assertHasDescription(String context) {
             assertThatDescription(startsWith(context));
             return this;
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/GradleDistribution.java b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/GradleDistribution.java
index f97a96b..0e31620 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/GradleDistribution.java
+++ b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/GradleDistribution.java
@@ -55,7 +55,7 @@ public class GradleDistribution implements MethodRule, TestFileContext, BasicGra
 
     @Override
     public String toString() {
-        return String.format("Gradle %s", new GradleVersion().getVersion());
+        return String.format("Gradle %s", GradleVersion.current().getVersion());
     }
 
     public boolean worksWith(Jvm jvm) {
@@ -95,7 +95,7 @@ public class GradleDistribution implements MethodRule, TestFileContext, BasicGra
     }
 
     public String getVersion() {
-        return new GradleVersion().getVersion();
+        return GradleVersion.current().getVersion();
     }
 
     public TestFile getBinDistribution() {
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/GradleDistributionExecuter.java b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/GradleDistributionExecuter.java
index 3e0be13..998c727 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/GradleDistributionExecuter.java
+++ b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/GradleDistributionExecuter.java
@@ -40,7 +40,7 @@ public class GradleDistributionExecuter extends AbstractGradleExecuter implement
     private boolean workingDirSet;
     private boolean userHomeSet;
 
-    private enum Executer {
+    public enum Executer {
         forking, embedded, daemon
     }
 
@@ -48,6 +48,10 @@ public class GradleDistributionExecuter extends AbstractGradleExecuter implement
         EXECUTER = Executer.valueOf(System.getProperty(EXECUTER_SYS_PROP, Executer.forking.toString()).toLowerCase());
     }
 
+    public static Executer getExecuter() {
+        return EXECUTER;
+    }
+
     public GradleDistributionExecuter(GradleDistribution dist) {
         this.dist = dist;
         reset();
@@ -106,8 +110,14 @@ public class GradleDistributionExecuter extends AbstractGradleExecuter implement
     }
 
     private <T extends ExecutionResult> T checkResult(T result) {
+        // Assert that nothing unexpected was logged
         result.assertOutputHasNoStackTraces();
         result.assertErrorHasNoStackTraces();
+        if (getExecutable() == null) {
+            // Assert that no temp files are left lying around
+            // Note: don't do this if a custom executable is used, as we don't know (and probably don't care) whether the executable cleans up or not
+            getTmpDir().assertIsEmptyDir();
+        }
         return result;
     }
 
@@ -136,14 +146,19 @@ public class GradleDistributionExecuter extends AbstractGradleExecuter implement
             boolean useDaemon = EXECUTER == Executer.daemon && getExecutable() == null;
             ForkingGradleExecuter forkingGradleExecuter = useDaemon ? new DaemonGradleExecuter(dist.getGradleHomeDir()) : new ForkingGradleExecuter(dist.getGradleHomeDir());
             copyTo(forkingGradleExecuter);
+            TestFile tmpDir = getTmpDir();
+            if (tmpDir != null) {
+                tmpDir.deleteDir().createDir();
+                forkingGradleExecuter.addGradleOpts(String.format("-Djava.io.tmpdir=%s", tmpDir));
+            }
             returnedExecuter = forkingGradleExecuter;
         }
 
         boolean settingsFound = false;
         for (
-                File dir = new TestFile(getWorkingDir()); dir != null && dist.isFileUnderTest(dir) && !settingsFound;
+                TestFile dir = new TestFile(getWorkingDir()); dir != null && dist.isFileUnderTest(dir) && !settingsFound;
                 dir = dir.getParentFile()) {
-            if (new File(dir, "settings.gradle").isFile()) {
+            if (dir.file("settings.gradle").isFile()) {
                 settingsFound = true;
             }
         }
@@ -153,4 +168,8 @@ public class GradleDistributionExecuter extends AbstractGradleExecuter implement
 
         return returnedExecuter;
     }
+
+    private TestFile getTmpDir() {
+        return new TestFile(dist.getUserHomeDir(), "tmp");
+    }
 }
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/HttpServer.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/HttpServer.groovy
index 8dc4149..286a16b 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/HttpServer.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/HttpServer.groovy
@@ -15,16 +15,26 @@
  */
 package org.gradle.integtests.fixtures
 
-import org.apache.commons.lang.StringUtils
+import java.security.Principal
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
+import org.junit.rules.MethodRule
+import org.junit.runners.model.FrameworkMethod
+import org.junit.runners.model.Statement
+import org.mortbay.jetty.Handler
+import org.mortbay.jetty.Request
 import org.mortbay.jetty.Server
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 import org.mortbay.jetty.handler.*
+import org.mortbay.jetty.security.*
+import org.mortbay.jetty.HttpHeaders
+import org.mortbay.jetty.MimeTypes
 
-class HttpServer {
+class HttpServer implements MethodRule {
     private Logger logger = LoggerFactory.getLogger(HttpServer.class)
     private final Server server = new Server(0)
-    private final ContextHandlerCollection collection = new ContextHandlerCollection()
+    private final HandlerCollection collection = new HandlerCollection()
 
     def HttpServer() {
         HandlerCollection handlers = new HandlerCollection()
@@ -41,20 +51,193 @@ class HttpServer {
         server.stop()
     }
 
+    Statement apply(Statement base, FrameworkMethod method, Object target) {
+        return new Statement() {
+            @Override
+            void evaluate() {
+                try {
+                    base.evaluate()
+                } finally {
+                    stop()
+                }
+            }
+        }
+    }
+
+    /**
+     * Adds a given file at the given URL. The source file can be either a file or a directory.
+     */
+    def allowGet(String path, File srcFile) {
+        allow(path, true, fileHandler(path, srcFile))
+    }
+
+    private AbstractHandler fileHandler(String path, File srcFile) {
+        return new AbstractHandler() {
+            void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) {
+                def file
+                if (request.pathInfo == path) {
+                    file = srcFile
+                } else {
+                    def relativePath = request.pathInfo.substring(path.length() + 1)
+                    file = new File(srcFile, relativePath)
+                }
+                if (!file.isFile()) {
+                    response.sendError(404, "'$target' does not exist")
+                    return
+                }
+                response.setDateHeader(HttpHeaders.LAST_MODIFIED, file.lastModified())
+                response.setContentLength((int)file.length())
+                response.setContentType(new MimeTypes().getMimeByExtension(file.name).toString())
+                response.outputStream.bytes = file.bytes
+            }
+        }
+    }
+
+    /**
+     * Adds a broken resource at the given URL.
+     */
+    def addBroken(String path) {
+        allow(path, true, new AbstractHandler() {
+            void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) {
+                response.sendError(500, "broken")
+            }
+        })
+    }
+
+    /**
+     * Allow one GET request for the given URL. Reads the request content from the given file.
+     */
+    def expectGet(String path, File srcFile) {
+        expect(path, true, fileHandler(path, srcFile))
+    }
+
+    /**
+     * Allow one PUT request for the given URL. Writes the request content to the given file.
+     */
+    def expectPut(String path, File destFile) {
+        expect(path, false, new AbstractHandler() {
+            void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) {
+                destFile.bytes = request.inputStream.bytes
+            }
+        })
+    }
+
     /**
-     * Adds a given file at the given URL.
+     * Allow one PUT request for the given URL, with the given credentials. Writes the request content to the given file.
      */
-    def add(String path, File srcFile) {
+    def expectPut(String path, String username, String password, File destFile) {
+        def realm = new TestUserRealm()
+        realm.username = username
+        realm.password = password
+        def constraint = new Constraint()
+        constraint.name = Constraint.__BASIC_AUTH
+        constraint.authenticate = true
+        constraint.roles = ['*'] as String[]
+        def constraintMapping = new ConstraintMapping()
+        constraintMapping.pathSpec = path
+        constraintMapping.constraint = constraint
+        def securityHandler = new SecurityHandler()
+        securityHandler.userRealm = realm
+        securityHandler.constraintMappings = [constraintMapping] as ConstraintMapping[]
+        securityHandler.authenticator = new BasicAuthenticator()
+        collection.addHandler(securityHandler)
+
+        expect(path, false, new AbstractHandler() {
+            void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) {
+                if (request.remoteUser != username) {
+                    response.sendError(500, 'unexpected username')
+                    return
+                }
+                destFile.bytes = request.inputStream.bytes
+            }
+        })
+    }
+
+    def expect(String path, boolean recursive, Handler handler) {
+        boolean run
+        add(path, recursive, new AbstractHandler() {
+            void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) {
+                if (run) {
+                    return
+                }
+                run = true
+                handler.handle(target, request, response, dispatch)
+                request.handled = true
+            }
+        })
+    }
+
+    def allow(String path, boolean recursive, Handler handler) {
+        add(path, recursive, new AbstractHandler() {
+            void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) {
+                handler.handle(target, request, response, dispatch)
+                request.handled = true
+            }
+        })
+    }
+
+    def add(String path, boolean recursive, Handler handler) {
         assert path.startsWith('/')
-        ContextHandler context = new ContextHandler()
-        String contextPath = StringUtils.substringBeforeLast(path, '/')
-        context.contextPath = contextPath ?: '/'
-        context.resourceBase = srcFile.parentFile.path
-        context.addHandler(new ResourceHandler())
-        collection.addHandler(context)
+        assert path == '/' || !path.endsWith('/')
+        def prefix = path == '/' ? '/' : path + '/'
+        collection.addHandler(new AbstractHandler() {
+            void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) {
+                boolean match = request.pathInfo == path || (recursive && request.pathInfo.startsWith(prefix))
+                if (match && !request.handled) {
+                    handler.handle(target, request, response, dispatch)
+                }
+            }
+        })
     }
 
     def int getPort() {
         return server.connectors[0].localPort
     }
+
+    static class TestUserRealm implements UserRealm {
+        String username
+        String password
+
+        Principal authenticate(String username, Object credentials, Request request) {
+            if (username == this.username && password == credentials) {
+                return getPrincipal(username)
+            }
+            return null
+        }
+
+        String getName() {
+            return "test"
+        }
+
+        Principal getPrincipal(String username) {
+            return new Principal() {
+                String getName() {
+                    return username
+                }
+            }
+        }
+
+        boolean reauthenticate(Principal user) {
+            return false
+        }
+
+        boolean isUserInRole(Principal user, String role) {
+            return false
+        }
+
+        void disassociate(Principal user) {
+        }
+
+        Principal pushRole(Principal user, String role) {
+            return user
+        }
+
+        Principal popRole(Principal user) {
+            return user
+        }
+
+        void logout(Principal user) {
+        }
+
+    }
 }
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/InProcessGradleExecuter.java b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/InProcessGradleExecuter.java
index c6c7481..5d83109 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/InProcessGradleExecuter.java
+++ b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/InProcessGradleExecuter.java
@@ -295,13 +295,28 @@ public class InProcessGradleExecuter extends AbstractGradleExecuter {
             return this;
         }
 
+        public ExecutionResult assertTaskSkipped(String taskPath) {
+            assertThat(skippedTasks, hasItem(taskPath));
+            return this;
+        }
+
         public ExecutionResult assertTasksNotSkipped(String... taskPaths) {
             Set<String> expected = new HashSet<String>(Arrays.asList(taskPaths));
-            Set<String> notSkipped = new HashSet<String>(plannedTasks);
-            notSkipped.removeAll(skippedTasks);
+            Set<String> notSkipped = getExecutedTasks();
             assertThat(notSkipped, equalTo(expected));
             return this;
         }
+
+        public ExecutionResult assertTaskNotSkipped(String taskPath) {
+            assertThat(getExecutedTasks(), hasItem(taskPath));
+            return this;
+        }
+
+        private Set<String> getExecutedTasks() {
+            Set<String> notSkipped = new HashSet<String>(plannedTasks);
+            notSkipped.removeAll(skippedTasks);
+            return notSkipped;
+        }
     }
 
     private static class InProcessExecutionFailure extends InProcessExecutionResult implements ExecutionFailure {
@@ -340,6 +355,16 @@ public class InProcessGradleExecuter extends AbstractGradleExecuter {
             return this;
         }
 
+        public ExecutionFailure assertHasNoCause() {
+            if (failure instanceof LocationAwareException) {
+                LocationAwareException exception = (LocationAwareException) failure;
+                assertThat(exception.getReportableCauses(), isEmpty());
+            } else {
+                assertThat(failure.getCause(), nullValue());
+            }
+            return this;
+        }
+
         public ExecutionFailure assertHasDescription(String context) {
             assertThatDescription(startsWith(context));
             return this;
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/IvyRepository.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/IvyRepository.groovy
new file mode 100644
index 0000000..7797ba8
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/IvyRepository.groovy
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+class IvyRepository {
+    final TestFile rootDir
+
+    IvyRepository(TestFile rootDir) {
+        this.rootDir = rootDir
+    }
+
+    String getPattern() {
+        return "[organisation]/[module]/[revision]/[artifact]-[revision].[ext]"
+    }
+
+    IvyModule module(String organisation, String module, Object revision = '1.0') {
+        def moduleDir = rootDir.file("$organisation/$module/$revision")
+        return new IvyModule(moduleDir, organisation, module, revision as String)
+    }
+}
+
+class IvyModule {
+    final TestFile moduleDir
+    final String organisation
+    final String module
+    final String revision
+
+    IvyModule(TestFile moduleDir, String organisation, String module, String revision) {
+        this.moduleDir = moduleDir
+        this.organisation = organisation
+        this.module = module
+        this.revision = revision
+    }
+
+    File getIvyFile() {
+        return moduleDir.file("ivy-${revision}.xml")
+    }
+
+    File getJarFile() {
+        return moduleDir.file("$module-${revision}.jar")
+    }
+
+    File publishArtifact() {
+        moduleDir.createDir()
+
+        ivyFile << """<?xml version="1.0" encoding="UTF-8"?>
+<ivy-module version="1.0">
+	<info organisation="${organisation}"
+		module="${module}"
+		revision="${revision}"
+	/>
+	<configurations>
+		<conf name="runtime" visibility="public"/>
+		<conf name="default" visibility="public" extends="runtime"/>
+	</configurations>
+	<publications>
+		<artifact name="${module}" type="jar" ext="jar" conf="*"/>
+	</publications>
+</ivy-module>
+        """
+
+        jarFile << "add some content so that file size isn't zero"
+
+        return jarFile
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/MavenRepository.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/MavenRepository.groovy
new file mode 100644
index 0000000..086ec07
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/MavenRepository.groovy
@@ -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.integtests.fixtures
+
+import org.gradle.util.TestFile
+
+class MavenRepository {
+    private final TestFile rootDir
+
+    MavenRepository(TestFile rootDir) {
+        this.rootDir = rootDir
+    }
+
+    MavenModule module(String groupId, String artifactId, Object version = '1.0') {
+        def artifactDir = rootDir.file("$groupId/$artifactId/$version")
+        return new MavenModule(artifactDir, groupId, artifactId, version as String)
+    }
+}
+
+class MavenModule {
+    final TestFile moduleDir
+    final String groupId
+    final String artifactId
+    final String version
+    private final List<String> dependencies = []
+
+    MavenModule(TestFile moduleDir, String groupId, String artifactId, String version) {
+        this.moduleDir = moduleDir
+        this.groupId = groupId
+        this.artifactId = artifactId
+        this.version = version
+    }
+
+    MavenModule dependsOn(String dependencyArtifactId) {
+        this.dependencies << dependencyArtifactId
+        return this
+    }
+
+    void assertArtifactsDeployed(String... names) {
+        for (name in names) {
+            moduleDir.file(name).assertIsFile()
+            moduleDir.file("${name}.md5").assertIsFile()
+            moduleDir.file("${name}.sha1").assertIsFile()
+        }
+    }
+
+    File publishArtifact() {
+        moduleDir.createDir()
+
+        def pomFile = new File(moduleDir, "$artifactId-${version}.pom")
+        pomFile << """
+<project xmlns="http://maven.apache.org/POM/4.0.0">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>$groupId</groupId>
+  <artifactId>$artifactId</artifactId>
+  <packaging>jar</packaging>
+  <version>$version</version>"""
+
+        dependencies.each { dependency ->
+            pomFile << """
+  <dependencies>
+    <dependency>
+      <groupId>$groupId</groupId>
+      <artifactId>$dependency</artifactId>
+      <version>1.0</version>
+    </dependency>
+  </dependencies>"""
+        }
+
+        pomFile << "\n</project>"
+
+        def jarFile = new File("$moduleDir/$artifactId-${version}.jar")
+        jarFile << "add some content so that file size isn't zero"
+
+        return jarFile
+    }
+
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/PreviousGradleVersionExecuter.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/PreviousGradleVersionExecuter.groovy
index 8908046..4451ec5 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/PreviousGradleVersionExecuter.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/PreviousGradleVersionExecuter.groovy
@@ -18,7 +18,7 @@ package org.gradle.integtests.fixtures
 import org.gradle.util.Jvm
 import org.gradle.util.TestFile
 import org.gradle.util.GradleVersion
-import org.gradle.api.tasks.wrapper.internal.DistributionLocator
+import org.gradle.util.DistributionLocator
 
 public class PreviousGradleVersionExecuter extends AbstractGradleExecuter implements BasicGradleDistribution {
     private final GradleDistribution dist
@@ -26,7 +26,7 @@ public class PreviousGradleVersionExecuter extends AbstractGradleExecuter implem
 
     PreviousGradleVersionExecuter(GradleDistribution dist, String version) {
         this.dist = dist
-        this.version = new GradleVersion(version)
+        this.version = GradleVersion.version(version)
     }
 
     def String toString() {
@@ -38,7 +38,7 @@ public class PreviousGradleVersionExecuter extends AbstractGradleExecuter implem
     }
 
     boolean worksWith(Jvm jvm) {
-        return version == new GradleVersion('0.9-rc-1') ? jvm.isJava6Compatible() : jvm.isJava5Compatible()
+        return version == GradleVersion.version('0.9-rc-1') ? jvm.isJava6Compatible() : jvm.isJava5Compatible()
     }
 
     protected ExecutionResult doRun() {
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/ScriptExecuter.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/ScriptExecuter.groovy
new file mode 100644
index 0000000..2daa475
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/ScriptExecuter.groovy
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.process.internal.ExecHandleBuilder
+import org.gradle.process.internal.ExecHandle
+import org.gradle.process.ExecResult
+import org.gradle.util.OperatingSystem
+
+class ScriptExecuter extends ExecHandleBuilder {
+    @Override
+    ExecHandle build() {
+        if (OperatingSystem.current().isWindows()) {
+            args = ['/c', executable.replace('/', File.separator)] + args
+            executable = 'cmd'
+        } else {
+            executable = "${workingDir}/${executable}"
+        }
+        return super.build()
+    }
+
+    ExecResult run() {
+        return build().start().waitForFinish()
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/TestClassExecutionResult.java b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/TestClassExecutionResult.java
index 46d9964..d389a97 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/TestClassExecutionResult.java
+++ b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/TestClassExecutionResult.java
@@ -25,6 +25,11 @@ public interface TestClassExecutionResult {
     TestClassExecutionResult assertTestsExecuted(String... testNames);
 
     /**
+     * Asserts that the given tests (and only the given tests) were skipped for the given test class.
+     */
+    TestClassExecutionResult assertTestsSkipped(String... testNames);
+
+    /**
      * Asserts that the given test passed.
      */
     TestClassExecutionResult assertTestPassed(String name);
@@ -35,6 +40,11 @@ public interface TestClassExecutionResult {
     TestClassExecutionResult assertTestFailed(String name, Matcher<? super String>... messageMatchers);
 
     /**
+     * Asserts that the given test was skipped.
+     */
+    TestClassExecutionResult assertTestSkipped(String name);
+
+    /**
      * Asserts that the given config method passed.
      */
     TestClassExecutionResult assertConfigMethodPassed(String name);
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/internal/AbstractAutoTestedSamplesTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/internal/AbstractAutoTestedSamplesTest.groovy
new file mode 100644
index 0000000..02acc4c
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/internal/AbstractAutoTestedSamplesTest.groovy
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.internal
+
+/**
+ * Author: Szczepan Faber, created at: 4/2/11
+ */
+class AbstractAutoTestedSamplesTest extends AbstractIntegrationTest{
+
+     void runSamplesFrom(String dir) {
+        def util = new AutoTestedSamplesUtil()
+        util.findSamples(dir) { file, sample ->
+            println "Found sample in $file"
+            def testFile = testFile('build.gradle')
+            testFile.text = sample
+            usingBuildFile(testFile).withTasks('tasks').withArguments("-s").run()
+        }
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/internal/AbstractIntegrationTest.java b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/internal/AbstractIntegrationTest.java
new file mode 100644
index 0000000..9a1bcae
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/internal/AbstractIntegrationTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.internal;
+
+import org.gradle.CacheUsage;
+import org.gradle.StartParameter;
+import org.gradle.integtests.fixtures.*;
+import org.gradle.util.TestFile;
+import org.gradle.util.TestFileContext;
+import org.junit.Rule;
+
+import java.io.File;
+
+public abstract class AbstractIntegrationTest implements TestFileContext {
+    @Rule public final GradleDistribution distribution = new GradleDistribution();
+    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter();
+
+    public TestFile getTestDir() {
+        return distribution.getTestDir();
+    }
+
+    public TestFile file(Object... path) {
+        return getTestDir().file(path);
+    }
+
+    public TestFile testFile(String name) {
+        return file(name);
+    }
+
+    private StartParameter startParameter() {
+        StartParameter parameter = new StartParameter();
+        parameter.setGradleUserHomeDir(distribution.getUserHomeDir());
+
+        parameter.setSearchUpwards(false);
+        parameter.setCacheUsage(CacheUsage.ON);
+        parameter.setCurrentDir(getTestDir());
+
+        return parameter;
+    }
+
+    protected GradleExecuter inTestDirectory() {
+        return inDirectory(getTestDir());
+    }
+
+    protected GradleExecuter inDirectory(File directory) {
+        return executer.inDirectory(directory);
+    }
+
+    protected GradleExecuter usingBuildFile(File file) {
+        return executer.usingBuildScript(file);
+    }
+
+    protected GradleExecuter usingBuildScript(String script) {
+        return executer.usingBuildScript(script);
+    }
+
+    protected GradleExecuter usingProjectDir(File projectDir) {
+        return executer.usingProjectDirectory(projectDir);
+    }
+
+    protected ArtifactBuilder artifactBuilder() {
+        return new GradleBackedArtifactBuilder(new InProcessGradleExecuter(startParameter()), getTestDir().file("artifacts"));
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/internal/AutoTestedSamplesUtil.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/internal/AutoTestedSamplesUtil.groovy
new file mode 100644
index 0000000..07d4648
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/internal/AutoTestedSamplesUtil.groovy
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.internal
+
+/**
+ * Author: Szczepan Faber, created at: 3/28/11
+ */
+class AutoTestedSamplesUtil {
+
+    void findSamples(String dir, Closure runner) {
+        def sources = findDir(dir)
+        def ant = new AntBuilder()
+
+        def list = ant.fileScanner {
+            fileset(dir: sources, includes: '**/*.groovy **/*.java')
+        }
+
+        list.each() { runSamplesFromFile(it, runner) }
+    }
+
+    String findDir(String dir) {
+        def workDir = System.getProperty("user.dir")
+        def candidates = [
+            "$workDir/$dir",        //when ran from IDEA
+            "$workDir/../../$dir"  //when ran from command line
+        ]
+        for (c in candidates) {
+            if (new File(c).exists()) {
+                return c
+            }
+        }
+        throw new RuntimeException("""Couldn't find the root folder :-( Please update the logic so that it detects the root folder correctly.
+I tried looking for a root folder here: $candidates
+""")
+    }
+
+    void runSamplesFromFile(File file, Closure runner) {
+        file.text.eachMatch(/(?ms).*?<pre autoTested.*?>(.*?)<\/pre>(.*?)/) {
+            def sample = it[1]
+            sample = sample.replaceAll(/(?m)^\s*?\*/, '')
+            try {
+                runner.call(file, sample)
+            } catch (Exception e) {
+                throw new RuntimeException("""
+*****
+Failed to execute sample:
+-File: $file
+-Sample:
+$sample
+-Problem: see the full stactrace below.
+*****
+""", e);
+            }
+        }
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/internal/IntegrationTestHint.java b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/internal/IntegrationTestHint.java
new file mode 100644
index 0000000..01f5ebf
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/integtests/fixtures/internal/IntegrationTestHint.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.internal;
+
+/**
+ * @author Szczepan Faber, @date: 25.03.11
+ */
+public class IntegrationTestHint extends RuntimeException {
+
+    public IntegrationTestHint(Throwable cause) {
+        super("****\n"
++"This test is one of the integration tests that requires specific tasks to be ran first.\n"
++"Please run: gradle ideVersionProperties binZip intTestImage publishLocalArchives\n"
++"If the problem persists after running tasks then it probably means it's a genuine test failure.\n"
++"****\n", cause);
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/maven/MavenProjectIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/maven/MavenProjectIntegrationTest.groovy
deleted file mode 100644
index e8391c6..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/maven/MavenProjectIntegrationTest.groovy
+++ /dev/null
@@ -1,63 +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.maven
-
-import org.gradle.integtests.fixtures.GradleDistribution
-import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.junit.Rule
-import org.junit.Test
-import org.gradle.integtests.fixtures.TestResources
-
-class MavenProjectIntegrationTest {
-    @Rule public final GradleDistribution dist = new GradleDistribution()
-    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
-    @Rule public final TestResources testResources = new TestResources()
-
-    @Test
-    public void handlesSubProjectsWithoutTheMavenPluginApplied() {
-        dist.testFile("settings.gradle").write("include 'subProject'");
-        dist.testFile("build.gradle") << '''
-            apply plugin: 'java'
-            apply plugin: 'maven'
-        '''
-        executer.withTaskList().run();
-    }
-
-    @Test
-    public void canDeployAProjectWithDependencyInMappedAndUnMappedConfiguration() {
-        executer.withTasks('uploadArchives').run()
-        def module = repo().module('group', 'root', 1.0)
-        module.assertArtifactsDeployed('root-1.0.jar')
-    }
-
-    @Test
-    public void canDeployAProjectWithNoMainArtifact() {
-        executer.withTasks('uploadArchives').run()
-        def module = repo().module('group', 'root', 1.0)
-        module.assertArtifactsDeployed('root-1.0-source.jar')
-    }
-
-    @Test
-    public void canDeployAProjectWithMetadataArtifacts() {
-        executer.withTasks('uploadArchives').run()
-        def module = repo().module('group', 'root', 1.0)
-        module.assertArtifactsDeployed('root-1.0.jar', 'root-1.0.jar.sig', 'root-1.0.pom', 'root-1.0.pom.sig')
-    }
-
-    def MavenRepository repo() {
-        new MavenRepository(dist.testFile('mavenRepo'))
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/maven/MavenRepository.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/maven/MavenRepository.groovy
deleted file mode 100644
index b036e34..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/maven/MavenRepository.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.integtests.maven
-
-import org.gradle.util.TestFile
-
-class MavenRepository {
-    final TestFile rootDir
-
-    MavenRepository(TestFile rootDir) {
-        this.rootDir = rootDir
-    }
-
-    MavenModule module(String group, String artifactId, Object version) {
-        TestFile moduleDir = rootDir.file(group, artifactId, version)
-        moduleDir.assertIsDir()
-        return new MavenModule(moduleDir)
-    }
-}
-
-class MavenModule {
-    final TestFile moduleDir
-
-    MavenModule(TestFile moduleDir) {
-        this.moduleDir = moduleDir
-    }
-
-    void assertArtifactsDeployed(String... names) {
-        for (name in names) {
-            moduleDir.file(name).assertIsFile()
-            moduleDir.file("${name}.md5").assertIsFile()
-            moduleDir.file("${name}.sha1").assertIsFile()
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/maven/MavenSnapshotIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/maven/MavenSnapshotIntegrationTest.groovy
deleted file mode 100644
index 6ec925c..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/maven/MavenSnapshotIntegrationTest.groovy
+++ /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.integtests.maven
-
-import org.gradle.integtests.fixtures.GradleDistribution
-import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.gradle.integtests.fixtures.HttpServer
-import org.gradle.integtests.fixtures.TestResources
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-
-/**
- * @author Hans Dockter
- */
-class MavenSnapshotIntegrationTest {
-    @Rule public GradleDistribution distribution = new GradleDistribution()
-    @Rule public GradleDistributionExecuter executer = new GradleDistributionExecuter()
-    @Rule public final TestResources testResources = new TestResources()
-
-    @Before
-    public void setup() {
-        distribution.requireOwnUserHomeDir()
-    }
-
-    @Test
-    public void retrievesAndCacheLocalSnapshot() {
-        def producerProject = distribution.testFile('producer.gradle')
-        def consumerProject = distribution.testFile('projectWithMavenSnapshots.gradle')
-
-        // Publish the first snapshot
-        executer.usingBuildScript(producerProject).withTasks('uploadArchives').run()
-
-        // Retrieve the first snapshot
-        executer.usingBuildScript(consumerProject).withTasks('retrieve').run()
-        def jarFile = distribution.testFile('build/testproject-1.0-SNAPSHOT.jar')
-        def snapshot = jarFile.assertIsFile().snapshot()
-
-        // Retrieve again should use cached snapshot
-        executer.usingBuildScript(consumerProject).withTasks('retrieve').run().assertTasksSkipped(':retrieve')
-        jarFile.assertHasNotChangedSince(snapshot)
-
-        // Publish the second snapshot
-        Thread.sleep(1100)
-        executer.usingBuildScript(producerProject).withTasks('uploadArchives').withArguments("-PemptyJar").run()
-
-        // Retrieve again should use updated snapshot
-        executer.usingBuildScript(consumerProject).withTasks('retrieve').run().assertTasksNotSkipped(':retrieve')
-        jarFile.assertHasChangedSince(snapshot)
-    }
-
-    @Test
-    public void retrievesAndCacheSnapshotViaHttp() {
-        HttpServer server = new HttpServer()
-        server.add('/repo', distribution.testFile('repo'))
-        server.start()
-        String repoUrl = "-PrepoUrl=http://localhost:${server.port}/repo"
-
-        def producerProject = distribution.testFile('producer.gradle')
-        def consumerProject = distribution.testFile('projectWithMavenSnapshots.gradle')
-
-        // Publish the first snapshot
-        executer.usingBuildScript(producerProject).withTasks('uploadArchives').run()
-
-        // Retrieve the first snapshot
-        executer.usingBuildScript(consumerProject).withTasks('retrieve').withArguments(repoUrl).run()
-        def jarFile = distribution.testFile('build/testproject-1.0-SNAPSHOT.jar')
-        def snapshot = jarFile.assertIsFile().snapshot()
-
-        // Publish the second snapshot
-        Thread.sleep(1100)
-        executer.usingBuildScript(producerProject).withTasks('uploadArchives').withArguments("-PemptyJar").run()
-
-        // Retrieve again should use cached snapshot
-        executer.usingBuildScript(consumerProject).withTasks('retrieve').withArguments(repoUrl).run().assertTasksSkipped(':retrieve')
-        jarFile.assertHasNotChangedSince(snapshot)
-
-        // Retrieve again with zero timeout should use updated snapshot
-        executer.usingBuildScript(consumerProject).withTasks('retrieve').withArguments("-PnoTimeout", repoUrl).run().assertTasksNotSkipped(':retrieve')
-        jarFile.assertHasChangedSince(snapshot)
-        
-        server.stop()
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/maven/SamplesMavenPomGenerationIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/maven/SamplesMavenPomGenerationIntegrationTest.groovy
deleted file mode 100644
index 454dd0e..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/maven/SamplesMavenPomGenerationIntegrationTest.groovy
+++ /dev/null
@@ -1,152 +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.maven
-
-import groovy.text.SimpleTemplateEngine
-import org.custommonkey.xmlunit.Diff
-import org.custommonkey.xmlunit.XMLAssert
-import org.custommonkey.xmlunit.examples.RecursiveElementNameAndTextQualifier
-import org.gradle.integtests.fixtures.GradleDistribution
-import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.gradle.util.Resources
-import org.gradle.util.TestFile
-import org.hamcrest.Matchers
-import org.junit.Assert
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import static org.junit.Assert.*
-import org.gradle.integtests.fixtures.Sample
-
-/**
- * @author Hans Dockter
- */
-class SamplesMavenPomGenerationIntegrationTest {
-    @Rule public final GradleDistribution dist = new GradleDistribution()
-    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
-
-    private TestFile pomProjectDir
-    private TestFile repoDir
-    private TestFile snapshotRepoDir
-
-    @Rule public Resources resources = new Resources();
-    @Rule public final Sample sample = new Sample('maven/pomGeneration')
-
-    @Before
-    public void setUp() {
-        pomProjectDir = sample.dir
-        repoDir = pomProjectDir.file('pomRepo');
-        snapshotRepoDir = pomProjectDir.file('snapshotRepo');
-    }
-    
-    @Test
-    public void checkWithNoCustomVersion() {
-        String version = '1.0'
-        String groupId = "gradle"
-        long start = System.currentTimeMillis();
-        executer.inDirectory(pomProjectDir).withTasks('clean', 'uploadArchives', 'install').run()
-        String repoPath = repoPath(groupId, version)
-        compareXmlWithIgnoringOrder(expectedPom(version, groupId),
-                pomFile(repoDir, repoPath, version).text)
-        repoDir.file("$repoPath/mywar-${version}.war").assertIsCopyOf(pomProjectDir.file("target/libs/mywar-${version}.war"))
-        pomProjectDir.file('build').assertDoesNotExist()
-        pomProjectDir.file('target').assertIsDir()
-        checkInstall(start, pomProjectDir, version, groupId)
-    }
-
-    @Test
-    public void checkWithCustomVersion() {
-        long start = System.currentTimeMillis();
-        String version = "1.0MVN"
-        String groupId = "deployGroup"
-        executer.inDirectory(pomProjectDir).withArguments("-PcustomVersion=${version}").withTasks('clean', 'uploadArchives', 'install').run()
-        String repoPath = repoPath(groupId, version)
-        compareXmlWithIgnoringOrder(expectedPom(version, groupId),
-                pomFile(repoDir, repoPath, version).text)
-        repoDir.file("$repoPath/mywar-${version}.war").assertIsCopyOf(pomProjectDir.file("target/libs/mywar-1.0.war"))
-        repoDir.file("$repoPath/mywar-${version}-javadoc.zip").assertIsCopyOf(pomProjectDir.file("target/distributions/mywar-1.0-javadoc.zip"))
-        pomProjectDir.file('build').assertDoesNotExist()
-        pomProjectDir.file('target').assertIsDir()
-        checkInstall(start, pomProjectDir, version, 'installGroup')
-    }
-
-    @Test
-    public void checkWithSnapshotVersion() {
-        String version = '1.0-SNAPSHOT'
-        String groupId = "deployGroup"
-        long start = System.currentTimeMillis();
-        executer.inDirectory(pomProjectDir).withArguments("-PcustomVersion=${version}").withTasks('clean', 'uploadArchives', 'install').run()
-        String repoPath = repoPath(groupId, version)
-        File pomFile = pomFile(snapshotRepoDir, repoPath, version)
-        compareXmlWithIgnoringOrder(expectedPom(version, groupId), pomFile.text)
-        new TestFile(new File(pomFile.absolutePath.replace(".pom", ".war"))).assertIsFile()
-        pomProjectDir.file('build').assertDoesNotExist()
-        pomProjectDir.file('target').assertIsDir()
-        checkInstall(start, pomProjectDir, version, 'installGroup')
-    }
-
-    @Test
-    public void writeNewPom() {
-        executer.inDirectory(pomProjectDir).withTasks('clean', 'writeNewPom').run()
-        compareXmlWithIgnoringOrder(expectedPom(null, null, 'pomGeneration/expectedNewPom.txt'),
-                pomProjectDir.file("target/newpom.xml").text)
-    }
-
-    @Test
-    public void writeDeployerPom() {
-        String version = '1.0'
-        String groupId = "gradle"
-        executer.inDirectory(pomProjectDir).withTasks('clean', 'writeDeployerPom').run()
-        compareXmlWithIgnoringOrder(expectedPom(version, groupId), pomProjectDir.file("target/deployerpom.xml").text)
-    }
-    
-    static String repoPath(String group, String version) {
-        "$group/mywar/$version"
-    }
-
-    static File pomFile(TestFile repoDir, String repoPath, String version) {
-        TestFile versionDir = repoDir.file(repoPath)
-        List matches = versionDir.listFiles().findAll { it.name.endsWith('.pom') }
-        assertEquals(1, matches.size())
-        matches[0]
-    }
-
-    void checkInstall(long start, TestFile pomProjectDir, String version, String groupId) {
-        TestFile localMavenRepo = new TestFile(pomProjectDir.file("target/localRepoPath.txt").text as File)
-        TestFile installedFile = localMavenRepo.file("$groupId/mywar/$version/mywar-${version}.war")
-        TestFile installedJavadocFile = localMavenRepo.file("$groupId/mywar/$version/mywar-${version}-javadoc.zip")
-        TestFile installedPom = localMavenRepo.file("$groupId/mywar/$version/mywar-${version}.pom")
-        installedFile.assertIsCopyOf(pomProjectDir.file("target/libs/mywar-1.0.war"))
-        installedJavadocFile.assertIsCopyOf(pomProjectDir.file("target/distributions/mywar-1.0-javadoc.zip"))
-        installedPom.assertIsFile()
-        Assert.assertTrue((start/2000) <= (installedFile.lastModified()/2000))
-        Assert.assertTrue((start/2000) <= (installedJavadocFile.lastModified()/2000))
-        compareXmlWithIgnoringOrder(expectedPom(version, groupId), installedPom.text)
-    }
-    
-    private String expectedPom(String version, String groupId, String path = 'pomGeneration/expectedPom.txt') {
-        SimpleTemplateEngine templateEngine = new SimpleTemplateEngine();
-        String text = resources.getResource(path).text
-        return templateEngine.createTemplate(text).make(version: version, groupId: groupId)
-    }
-
-    private static void compareXmlWithIgnoringOrder(String expectedXml, String actualXml) {
-        Diff diff = new Diff(expectedXml, actualXml)
-        diff.overrideElementQualifier(new RecursiveElementNameAndTextQualifier())
-        XMLAssert.assertXMLEqual(diff, true);
-        Assert.assertThat(actualXml, Matchers.startsWith(String.format('<?xml version="1.0" encoding="UTF-8"?>')))
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/maven/SamplesMavenQuickstartIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/maven/SamplesMavenQuickstartIntegrationTest.groovy
deleted file mode 100644
index 2e32d7d..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/maven/SamplesMavenQuickstartIntegrationTest.groovy
+++ /dev/null
@@ -1,96 +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.maven
-
-import groovy.text.SimpleTemplateEngine
-import org.custommonkey.xmlunit.Diff
-import org.custommonkey.xmlunit.XMLAssert
-import org.custommonkey.xmlunit.examples.RecursiveElementNameAndTextQualifier
-import org.gradle.integtests.fixtures.GradleDistribution
-import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.gradle.integtests.fixtures.Sample
-import org.gradle.util.Resources
-import org.gradle.util.TestFile
-import org.junit.Assert
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import static org.junit.Assert.assertEquals
-
-/**
- * @author Hans Dockter
- */
-class SamplesMavenQuickstartIntegrationTest {
-    @Rule public final GradleDistribution dist = new GradleDistribution()
-    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
-    @Rule public Resources resources = new Resources();
-    @Rule public final Sample sample = new Sample('maven/quickstart')
-
-    private TestFile pomProjectDir
-    private TestFile repoDir
-
-    @Before
-    public void setUp() {
-        pomProjectDir = sample.dir
-        repoDir = pomProjectDir.file('pomRepo');
-    }
-
-    @Test
-    public void checkDeployAndInstall() {
-        String version = '1.0'
-        String groupId = "gradle"
-        long start = System.currentTimeMillis();
-        executer.inDirectory(pomProjectDir).withTasks('clean', 'uploadArchives', 'install').run()
-        String repoPath = repoPath(groupId, version)
-        compareXmlWithIgnoringOrder(expectedPom(version, groupId),
-                pomFile(repoDir, repoPath, version).text)
-        repoDir.file("$repoPath/quickstart-1.0.jar").assertIsCopyOf(pomProjectDir.file('build/libs/quickstart-1.0.jar'))
-        checkInstall(start, pomProjectDir, version, groupId)
-    }
-
-    static String repoPath(String group, String version) {
-        "$group/quickstart/$version"
-    }
-
-    static File pomFile(TestFile repoDir, String repoPath, String version) {
-        TestFile versionDir = repoDir.file(repoPath)
-        List matches = versionDir.listFiles().findAll { it.name.endsWith('.pom') }
-        assertEquals(1, matches.size())
-        matches[0]
-    }
-
-    void checkInstall(long start, TestFile pomProjectDir, String version, String groupId) {
-        TestFile localMavenRepo = new TestFile(pomProjectDir.file("build/localRepoPath.txt").text as File)
-        TestFile installedFile = localMavenRepo.file("$groupId/quickstart/$version/quickstart-${version}.jar")
-        TestFile installedPom = localMavenRepo.file("$groupId/quickstart/$version/quickstart-${version}.pom")
-        installedFile.assertIsCopyOf(pomProjectDir.file('build/libs/quickstart-1.0.jar'))
-        installedPom.assertIsFile()
-        Assert.assertTrue((start/2000) <= (installedFile.lastModified()/2000));
-        compareXmlWithIgnoringOrder(expectedPom(version, groupId), installedPom.text)
-    }
-
-    private String expectedPom(String version, String groupId) {
-        SimpleTemplateEngine templateEngine = new SimpleTemplateEngine();
-        String text = resources.getResource('pomGeneration/expectedQuickstartPom.txt').text
-        return templateEngine.createTemplate(text).make(version: version, groupId: groupId)
-    }
-
-    private static void compareXmlWithIgnoringOrder(String expectedXml, String actualXml) {
-        Diff diff = new Diff(expectedXml, actualXml)
-        diff.overrideElementQualifier(new RecursiveElementNameAndTextQualifier())
-        XMLAssert.assertXMLEqual(diff, true);
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/testng/TestNGExecutionResult.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/testng/TestNGExecutionResult.groovy
deleted file mode 100644
index 9a6f441..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/testng/TestNGExecutionResult.groovy
+++ /dev/null
@@ -1,157 +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 assertTestSkipped(String name) {
-        def testMethodNode = findTestMethod(name)
-        assertEquals('SKIP', testMethodNode. at status as String)
-        this
-    }
-
-    TestClassExecutionResult assertTestFailed(String name, Matcher<? super String>... messageMatchers) {
-        def testMethodNode = findTestMethod(name)
-        assertEquals('FAIL', testMethodNode. at status as String)
-
-        def exceptions = testMethodNode.exception
-        assertThat(exceptions.size(), equalTo(messageMatchers.length))
-
-        for (int i = 0; i < messageMatchers.length; i++) {
-            assertThat(exceptions[i].message[0].text().trim(), messageMatchers[i])
-        }
-        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/core/src/integTest/groovy/org/gradle/integtests/tooling/SamplesToolingApiIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/tooling/SamplesToolingApiIntegrationTest.groovy
deleted file mode 100644
index db72263..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/tooling/SamplesToolingApiIntegrationTest.groovy
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.tooling
-
-import org.gradle.integtests.fixtures.GradleDistribution
-import org.junit.Rule
-import spock.lang.Specification
-import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.gradle.integtests.fixtures.Sample
-
-class SamplesToolingApiIntegrationTest extends Specification {
-    @Rule public final GradleDistribution distribution = new GradleDistribution()
-    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
-    @Rule public final Sample sample = new Sample('toolingApi')
-
-    def canUseToolingApiToDetermineProjectClasspath() {
-        Properties props = new Properties()
-        props['toolingApiRepo'] = distribution.libsRepo.toURI().toString()
-        props['gradleDistribution'] = distribution.gradleHomeDir.toString()
-        sample.dir.file('gradle.properties').withOutputStream {outstr ->
-            props.store(outstr, 'props')
-        }
-
-        when:
-        def result = executer.inDirectory(sample.dir).withTasks('run').run()
-
-        then:
-        result.output.contains("gradle-tooling-api-${distribution.version}.jar")
-        result.output.contains("gradle-core-${distribution.version}.jar")
-        result.output.contains("gradle-wrapper-${distribution.version}.jar")
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiEclipseIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiEclipseIntegrationTest.groovy
deleted file mode 100644
index 5b35c82..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiEclipseIntegrationTest.groovy
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.tooling
-
-import org.gradle.tooling.BuildConnection
-import org.gradle.tooling.GradleConnector
-import org.gradle.tooling.model.ExternalDependency
-import org.gradle.tooling.model.eclipse.EclipseBuild
-import org.gradle.tooling.model.eclipse.EclipseProject
-
-class ToolingApiEclipseIntegrationTest extends ToolingApiSpecification {
-
-    def canBuildEclipseClasspathModelForABuild() {
-        def projectDir = dist.testDir
-        projectDir.file('build.gradle').text = '''
-            apply plugin: 'java'
-            repositories { mavenCentral() }
-            dependencies {
-                compile 'commons-lang:commons-lang:2.5'
-                runtime 'commons-io:commons-io:1.4'
-            }
-'''
-
-        when:
-        GradleConnector connector = GradleConnector.newConnector()
-        BuildConnection connection = connector.forProjectDirectory(projectDir).connect()
-        EclipseBuild build = connection.getModel(EclipseBuild.class)
-        EclipseProject rootProject = build.getRootProject()
-        connector.close()
-
-        then:
-        rootProject != null
-        rootProject.classpath.size() == 2
-        rootProject.classpath[0] instanceof ExternalDependency
-        rootProject.classpath[0].file.name == 'commons-io-1.4.jar'
-        rootProject.classpath[1] instanceof ExternalDependency
-        rootProject.classpath[1].file.name == 'commons-lang-2.5.jar'
-    }
-
-    def canBuildEclipseProjectHierarchyForAMultiProjectBuild() {
-        def projectDir = dist.testDir
-        projectDir.file('settings.gradle').text = '''
-            include "child1", "child2", "child1:child1"
-            rootProject.name = 'root'
-'''
-
-        when:
-        GradleConnector connector = GradleConnector.newConnector()
-        BuildConnection connection = connector.forProjectDirectory(projectDir).connect()
-        EclipseBuild build = connection.getModel(EclipseBuild.class)
-        EclipseProject rootProject = build.getRootProject()
-        connector.close()
-
-        then:
-        rootProject != null
-        rootProject.name == 'root'
-        rootProject.childProjects.size() == 2
-        rootProject.childProjects[0].name == 'child1'
-        rootProject.childProjects[0].childProjects.size() == 1
-
-        rootProject.childProjects[0].childProjects[0].name == 'child1'
-        rootProject.childProjects[0].childProjects[0].childProjects.size() == 0
-
-        rootProject.childProjects[1].name == 'child2'
-        rootProject.childProjects[1].childProjects.size() == 0
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiIntegrationTest.groovy
deleted file mode 100644
index 8773b60..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiIntegrationTest.groovy
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.tooling
-
-import org.gradle.tooling.BuildConnection
-import org.gradle.tooling.GradleConnector
-import org.gradle.tooling.model.Build
-import org.gradle.util.GradleVersion
-
-class ToolingApiIntegrationTest extends ToolingApiSpecification {
-    def canUseToolingApiWithoutSpecifyingADistributionToUse() {
-        def projectDir = dist.testDir
-        projectDir.file('build.gradle').text = "assert gradle.gradleVersion == '${new GradleVersion().version}'"
-
-        when:
-        GradleConnector connector = GradleConnector.newConnector()
-        BuildConnection connection = connector.forProjectDirectory(projectDir).connect()
-        Build model = connection.getModel(Build.class)
-        connector.close()
-
-        then:
-        model != null
-    }
-
-    def canSpecifyAGradleInstallationToUse() {
-        def projectDir = dist.testDir
-        projectDir.file('build.gradle').text = "assert gradle.gradleVersion == '${new GradleVersion().version}'"
-
-        when:
-        GradleConnector connector = GradleConnector.newConnector()
-        BuildConnection connection = connector.useInstallation(dist.gradleHomeDir).forProjectDirectory(projectDir).connect()
-        Build model = connection.getModel(Build.class)
-        connector.close()
-
-        then:
-        model != null
-    }
-
-    def canSpecifyAGradleDistributionToUse() {
-        def projectDir = dist.testDir
-        projectDir.file('build.gradle').text = "assert gradle.gradleVersion == '${new GradleVersion().version}'"
-
-        when:
-        GradleConnector connector = GradleConnector.newConnector()
-        BuildConnection connection = connector.useDistribution(dist.binDistribution.toURI()).forProjectDirectory(projectDir).connect()
-        Build model = connection.getModel(Build.class)
-        connector.close()
-
-        then:
-        model != null
-    }
-
-    def canSpecifyAGradleVersionToUse() {
-        def projectDir = dist.testDir
-        projectDir.file('build.gradle').text = "assert gradle.gradleVersion == '${new GradleVersion().version}'"
-
-        when:
-        GradleConnector connector = GradleConnector.newConnector()
-        BuildConnection connection = connector.useGradleVersion(new GradleVersion().version).forProjectDirectory(projectDir).connect()
-        Build model = connection.getModel(Build.class)
-        connector.close()
-
-        then:
-        model != null
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiSpecification.groovy b/subprojects/core/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiSpecification.groovy
deleted file mode 100644
index f9c1757..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiSpecification.groovy
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.tooling
-
-import spock.lang.Specification
-import org.gradle.tooling.internal.consumer.DistributionFactory
-import org.junit.Rule
-import org.gradle.util.SetSystemProperties
-import org.gradle.integtests.fixtures.GradleDistribution
-
-class ToolingApiSpecification extends Specification {
-    @Rule public final SetSystemProperties sysProperties = new SetSystemProperties()
-    @Rule public final GradleDistribution dist = new GradleDistribution()
-
-    def setup() {
-        System.properties[DistributionFactory.USE_CLASSPATH_AS_DISTRIBUTION] = 'true'
-    }
-}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/CommandLineIntegrationTest/shared/build.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/CommandLineIntegrationTest/shared/build.gradle
deleted file mode 100644
index 7e2220e..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/CommandLineIntegrationTest/shared/build.gradle
+++ /dev/null
@@ -1,16 +0,0 @@
-
-task checkGradleUserHomeViaSystemEnv << {
-    assert gradle.gradleUserHomeDir == file('customUserHome')
-}
-
-task checkDefaultGradleUserHome<< {
-    assert gradle.gradleUserHomeDir == new File(System.properties['user.home'], ".gradle")
-}
-
-task checkSystemPropertyGradleUserHomeHasPrecedence << {
-    assert gradle.gradleUserHomeDir == file('systemPropCustomUserHome')
-}
-
-task checkJavaHome << {
-    assert Jvm.current().javaHome == file(expectedJavaHome)
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/integTest/java/org/gradle/SomeClass.java b/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/integTest/java/org/gradle/SomeClass.java
deleted file mode 100644
index ad3b8c3..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/integTest/java/org/gradle/SomeClass.java
+++ /dev/null
@@ -1,3 +0,0 @@
-package org.gradle;
-
-public class SomeClass {}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiClasspath.xml b/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiClasspath.xml
deleted file mode 100644
index 1ce57b6..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiClasspath.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<classpath>
-	<classpathentry kind="output" path="bin"/>
-	<classpathentry kind="src" path="src/integTest/java"/>
-	<classpathentry kind="src" path="src/main/resources"/>
-	<classpathentry kind="src" path="src/main/java"/>
-	<classpathentry kind="src" path="src/test/resources"/>
-	<classpathentry kind="src" path="src/test/java"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER" exported="true"/>
-	<classpathentry sourcepath="@CACHE_DIR@/commons-collections/commons-collections/sources/commons-collections-3.2-sources.jar" kind="lib"
-					path="@CACHE_DIR@/commons-collections/commons-collections/jars/commons-collections-3.2.jar" exported="true"/>
-	<classpathentry sourcepath="@CACHE_DIR@/junit/junit/sources/junit-4.7-sources.jar" kind="lib" path="@CACHE_DIR@/junit/junit/jars/junit-4.7.jar" exported="true"/>
-</classpath>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiProject.xml b/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiProject.xml
deleted file mode 100644
index d96c07f..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiProject.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<projectDescription>
-	<name>api</name>
-	<comment/>
-	<projects/>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-	</natures>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments/>
-		</buildCommand>
-	</buildSpec>
-	<links/>
-</projectDescription>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/groovyprojectProject.xml b/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/groovyprojectProject.xml
deleted file mode 100644
index 34486ca..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/groovyprojectProject.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<projectDescription>
-	<name>groovyproject</name>
-	<comment/>
-	<projects/>
-	<natures>
-		<nature>org.eclipse.jdt.groovy.core.groovyNature</nature>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-	</natures>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments/>
-		</buildCommand>
-	</buildSpec>
-	<links/>
-</projectDescription>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/javabaseprojectProject.xml b/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/javabaseprojectProject.xml
deleted file mode 100644
index f67ee66..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/javabaseprojectProject.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<projectDescription>
-	<name>javabaseproject</name>
-	<comment/>
-	<projects/>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-	</natures>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments/>
-		</buildCommand>
-	</buildSpec>
-	<links/>
-</projectDescription>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/masterProject.xml b/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/masterProject.xml
deleted file mode 100644
index a3e9c8d..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/masterProject.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<projectDescription>
-	<name>master</name>
-	<comment/>
-	<projects/>
-	<natures/>
-	<buildSpec/>
-	<links/>
-</projectDescription>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppJava6Project.xml b/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppJava6Project.xml
deleted file mode 100644
index e25b5d6..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppJava6Project.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<projectDescription>
-	<name>webAppJava6</name>
-	<comment/>
-	<projects/>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-		<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
-		<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
-	</natures>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments/>
-		</buildCommand>
-		<buildCommand>
-			<name>org.eclipse.wst.common.project.facet.core.builder</name>
-			<arguments/>
-		</buildCommand>
-		<buildCommand>
-			<name>org.eclipse.wst.validation.validationbuilder</name>
-			<arguments/>
-		</buildCommand>
-	</buildSpec>
-	<links/>
-</projectDescription>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppJava6WtpComponent.xml b/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppJava6WtpComponent.xml
deleted file mode 100644
index b6ab7dc..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppJava6WtpComponent.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<project-modules id="moduleCoreId" project-version="2.0">
-	<wb-module deploy-name="webAppJava6">
-		<property name="java-output-path" value="build/classes/main"/>
-		<property name="context-root" value="webAppJava6"/>
-		<wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/java"/>
-		<wb-resource deploy-path="/" source-path="src/main/webapp"/>
-	</wb-module>
-</project-modules>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppWithVarsProject.xml b/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppWithVarsProject.xml
deleted file mode 100644
index 4cf73e3..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppWithVarsProject.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<projectDescription>
-	<name>webAppWithVars</name>
-	<comment/>
-	<projects/>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-		<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
-		<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
-	</natures>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments/>
-		</buildCommand>
-		<buildCommand>
-			<name>org.eclipse.wst.common.project.facet.core.builder</name>
-			<arguments/>
-		</buildCommand>
-		<buildCommand>
-			<name>org.eclipse.wst.validation.validationbuilder</name>
-			<arguments/>
-		</buildCommand>
-	</buildSpec>
-	<links/>
-</projectDescription>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppWithVarsWtpComponent.xml b/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppWithVarsWtpComponent.xml
deleted file mode 100644
index 24c0b21..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppWithVarsWtpComponent.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<project-modules id="moduleCoreId" project-version="2.0">
-	<wb-module deploy-name="webAppWithVars">
-		<property name="java-output-path" value="build/classes/main"/>
-		<property name="context-root" value="webAppWithVars"/>
-		<wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/java"/>
-		<wb-resource deploy-path="/" source-path="src/main/webapp"/>
-		<dependent-module deploy-path="/WEB-INF/lib" handle="module:/classpath/var/GRADLE_USER_HOME//cache/commons-lang/commons-lang/jars/commons-lang-2.5.jar">
-			<dependency-type>uses</dependency-type>
-		</dependent-module>
-	</wb-module>
-</project-modules>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webserviceProject.xml b/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webserviceProject.xml
deleted file mode 100644
index 3baf8a8..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webserviceProject.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<projectDescription>
-	<name>webservice</name>
-	<comment/>
-	<projects/>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-		<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
-		<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
-	</natures>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments/>
-		</buildCommand>
-		<buildCommand>
-			<name>org.eclipse.wst.common.project.facet.core.builder</name>
-			<arguments/>
-		</buildCommand>
-		<buildCommand>
-			<name>org.eclipse.wst.validation.validationbuilder</name>
-			<arguments/>
-		</buildCommand>
-	</buildSpec>
-	<links/>
-</projectDescription>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webserviceWtpComponent.xml b/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webserviceWtpComponent.xml
deleted file mode 100644
index 6878335..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webserviceWtpComponent.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-<project-modules id="moduleCoreId" project-version="2.0">
-	<wb-module deploy-name="webservice">
-		<property name="context-root" value="webservice"/>
-		<property name="java-output-path" value="build/classes/main"/>
-		<wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/java"/>
-		<wb-resource deploy-path="/" source-path="src/main/webapp"/>
-		<dependent-module deploy-path="/WEB-INF/lib" handle="module:/resource/api/api">
-			<dependency-type>uses</dependency-type>
-		</dependent-module>
-		<dependent-module deploy-path="/WEB-INF/lib" handle="module:/classpath/lib/@CACHE_DIR@/commons-lang/commons-lang/jars/commons-lang-2.5.jar">
-			<dependency-type>uses</dependency-type>
-		</dependent-module>
-	</wb-module>
-</project-modules>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/master/build.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/master/build.gradle
deleted file mode 100644
index 484f36a..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/master/build.gradle
+++ /dev/null
@@ -1,99 +0,0 @@
-import org.custommonkey.xmlunit.Diff
-import org.custommonkey.xmlunit.ElementNameAndAttributeQualifier
-import org.custommonkey.xmlunit.XMLAssert
-import junit.framework.AssertionFailedError
-import org.junit.ComparisonFailure
-
-buildscript {
-    repositories {
-        mavenCentral()
-    }
-    dependencies {
-        classpath 'xmlunit:xmlunit:1.3'
-    }
-}
-
-defaultTasks 'eclipse', 'cleanEclipse'
-
-allprojects {
-    apply plugin: 'eclipse'
-}
-
-subprojects {
-    repositories {
-        mavenCentral()
-    }
-
-    group = 'org.gradle'
-    version = '1.0'
-}
-
-allprojects {
-    afterEvaluate { p ->
-        configure(p) {
-            eclipseProject.doLast {
-                compareXmlWithIgnoringOrder(getExpectedXml(file("$rootDir/../expectedFiles/${project.name}Project.xml")),
-                        file(".project").text)
-            }
-
-            if (p.hasProperty('eclipseClasspath')) {
-                eclipseClasspath {
-                    downloadJavadoc = true
-                    doLast {
-                        compareXmlWithIgnoringOrder(getExpectedXml(file("$rootDir/../expectedFiles/${project.name}Classpath.xml")),
-                                file(".classpath").text)
-                    }
-                }
-            }
-
-            if (p.hasProperty('eclipseJdt')) {
-                eclipseJdt {
-                    doLast {
-                        compareProperties(getExpectedXml(file("$rootDir/../expectedFiles/${project.name}Jdt.properties")),
-                                file(".settings/org.eclipse.jdt.core.prefs").text)
-                    }
-                }
-            }
-
-            if (p.hasProperty('eclipseWtp')) {
-                eclipseWtp {
-                    doLast {
-                        compareXmlWithIgnoringOrder(getExpectedXml(file("$rootDir/../expectedFiles/${project.name}WtpComponent.xml")),
-                                file(".settings/org.eclipse.wst.common.component").text)
-                        compareXmlWithIgnoringOrder(getExpectedXml(file("$rootDir/../expectedFiles/${project.name}WtpFacet.xml")),
-                                file(".settings/org.eclipse.wst.common.project.facet.core.xml").text)
-                    }
-                }
-            }
-            cleanEclipse.doLast {
-                assert !file(".classpath").exists()
-                assert !file(".project").exists()
-                assert !file('.settings').exists() || file('.settings').listFiles().length == 0
-            }
-        }
-    }
-}
-
-void compareProperties(String expectedProperties, String actualProperties) {
-    Properties expected = new Properties()
-    expected.load(new ByteArrayInputStream(expectedProperties.bytes))
-    Properties actual = new Properties()
-    actual.load(new ByteArrayInputStream(actualProperties.bytes))
-    assert expected == actual
-}
-
-void compareXmlWithIgnoringOrder(String expectedXml, String actualXml) {
-    Diff diff = new Diff(expectedXml, actualXml)
-    diff.overrideElementQualifier(new ElementNameAndAttributeQualifier())
-    try {
-        XMLAssert.assertXMLEqual(diff, true)
-    } catch (AssertionFailedError error) {
-        throw new ComparisonFailure("Unexpected content for generated file: ${error.message}", expectedXml, actualXml).initCause(error)
-    }
-}
-
-String getExpectedXml(File file) {
-    def cache = new File(gradle.gradleUserHomeDir, "/cache").absolutePath.replace(File.separator, '/')
-    return file.text.replace('@CACHE_DIR@', cache)
-}
-
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppJava6/src/main/webapp/index.html b/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppJava6/src/main/webapp/index.html
deleted file mode 100644
index c85eebb..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppJava6/src/main/webapp/index.html
+++ /dev/null
@@ -1 +0,0 @@
-<p>index page.</p>
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppWithVars/build.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppWithVars/build.gradle
deleted file mode 100644
index 15a91df..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppWithVars/build.gradle
+++ /dev/null
@@ -1,9 +0,0 @@
-apply plugin: 'war'
-
-dependencies {
-    runtime "commons-lang:commons-lang:2.5"
-    testCompile 'junit:junit:4.7'
-}
-
-eclipseClasspath.variables = ['GRADLE_USER_HOME': gradle.gradleUserHomeDir]
-eclipseWtp.variables = ['GRADLE_USER_HOME': gradle.gradleUserHomeDir]
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/api/api.iml.xml b/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/api/api.iml.xml
deleted file mode 100644
index 1127e08..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/api/api.iml.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<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$/out/production/api"/>
-    <output-test url="file://$MODULE_DIR$/out/test/api"/>
-    <orderEntry type="module-library" exported="" scope="RUNTIME">
-      <library>
-        <CLASSES>
-          <root url="jar://@CACHE_DIR@/commons-collections/commons-collections/jars/commons-collections-3.2.jar!/"/>
-        </CLASSES>
-        <JAVADOC/>
-        <SOURCES>
-          <root url="jar://@CACHE_DIR@/commons-collections/commons-collections/sources/commons-collections-3.2-sources.jar!/"/>
-        </SOURCES>
-      </library>
-    </orderEntry>
-    <orderEntry type="module-library" scope="TEST">
-      <library>
-        <CLASSES>
-          <root url="jar://@CACHE_DIR@/junit/junit/jars/junit-4.7.jar!/"/>
-        </CLASSES>
-        <JAVADOC/>
-        <SOURCES>
-          <root url="jar://@CACHE_DIR@/junit/junit/sources/junit-4.7-sources.jar!/"/>
-        </SOURCES>
-      </library>
-    </orderEntry>
-  </component>
-  <component name="ModuleRootManager"/>
-</module>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/root.iml.xml b/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/root.iml.xml
deleted file mode 100644
index bd2993e..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/root.iml.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<module relativePaths="true" type="JAVA_MODULE" version="4">
-  <component name="NewModuleRootManager">
-    <exclude-output/>
-    <orderEntry type="inheritedJdk"/>
-    <content url="file://$MODULE_DIR$/">
-      <excludeFolder url="file://$MODULE_DIR$/.gradle"/>
-      <excludeFolder url="file://$MODULE_DIR$/build"/>
-    </content>
-    <orderEntry type="sourceFolder" forTests="false"/>
-  </component>
-  <component name="ModuleRootManager"/>
-</module>
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webservice/webservice.iml.xml b/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webservice/webservice.iml.xml
deleted file mode 100644
index b2d50ba..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webservice/webservice.iml.xml
+++ /dev/null
@@ -1,76 +0,0 @@
-<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/resources" isTestSource="false"/>
-      <sourceFolder url="file://$MODULE_DIR$/src/main/java" 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$/out/production/webservice"/>
-    <output-test url="file://$MODULE_DIR$/out/test/webservice"/>
-    <orderEntry type="module-library" exported="">
-      <library>
-        <CLASSES>
-          <root url="jar://@CACHE_DIR@/org.slf4j/slf4j-api/jars/slf4j-api-1.5.8.jar!/"/>
-        </CLASSES>
-        <JAVADOC/>
-        <SOURCES>
-          <root url="jar://@CACHE_DIR@/org.slf4j/slf4j-api/sources/slf4j-api-1.5.8-sources.jar!/"/>
-        </SOURCES>
-      </library>
-    </orderEntry>
-    <orderEntry type="module-library" exported="">
-      <library>
-        <CLASSES>
-          <root url="jar://$MODULE_DIR$/lib/compile-1.0.jar!/"/>
-        </CLASSES>
-        <JAVADOC/>
-        <SOURCES/>
-      </library>
-    </orderEntry>
-    <orderEntry type="module" module-name="api" exported=""/>
-    <orderEntry type="module-library" exported="" scope="RUNTIME">
-      <library>
-        <CLASSES>
-          <root url="jar://@CACHE_DIR@/commons-lang/commons-lang/jars/commons-lang-2.4.jar!/"/>
-        </CLASSES>
-        <JAVADOC>
-          <root url="jar://@CACHE_DIR@/commons-lang/commons-lang/javadocs/commons-lang-2.4-javadoc.jar!/"/>
-        </JAVADOC>
-        <SOURCES>
-          <root url="jar://@CACHE_DIR@/commons-lang/commons-lang/sources/commons-lang-2.4-sources.jar!/"/>
-        </SOURCES>
-      </library>
-    </orderEntry>
-    <orderEntry type="module-library" exported="" scope="RUNTIME">
-      <library>
-        <CLASSES>
-          <root url="jar://@CACHE_DIR@/commons-io/commons-io/jars/commons-io-1.2.jar!/"/>
-        </CLASSES>
-        <JAVADOC>
-          <root url="jar://@CACHE_DIR@/commons-io/commons-io/javadocs/commons-io-1.2-javadoc.jar!/"/>
-        </JAVADOC>
-        <SOURCES>
-          <root url="jar://@CACHE_DIR@/commons-io/commons-io/sources/commons-io-1.2-sources.jar!/"/>
-        </SOURCES>
-      </library>
-    </orderEntry>
-    <orderEntry type="module-library" scope="TEST">
-      <library>
-        <CLASSES>
-          <root url="jar://@CACHE_DIR@/junit/junit/jars/junit-4.7.jar!/"/>
-        </CLASSES>
-        <JAVADOC/>
-        <SOURCES>
-          <root url="jar://@CACHE_DIR@/junit/junit/sources/junit-4.7-sources.jar!/"/>
-        </SOURCES>
-      </library>
-    </orderEntry>
-  </component>
-  <component name="ModuleRootManager"/>
-</module>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/webservice/build.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/webservice/build.gradle
deleted file mode 100644
index c1c707b..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/webservice/build.gradle
+++ /dev/null
@@ -1,15 +0,0 @@
-apply plugin: 'war'
-
-version = '2.5'
-
-dependencies {
-    providedCompile 'org.slf4j:slf4j-api:1.5.8 at jar'
-    compile project(':api'), files("$projectDir/lib/compile-1.0.jar")
-    runtime module("commons-lang:commons-lang:2.4") {
-        dependency("commons-io:commons-io:1.2")
-    }
-}
-
-cleanIdea.doLast {
-    assert !file("webservice/webservice.iml").isFile()
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/overwritesExistingDependencies/expectedFiles/root.iml.xml b/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/overwritesExistingDependencies/expectedFiles/root.iml.xml
deleted file mode 100644
index 21dd9f0..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/overwritesExistingDependencies/expectedFiles/root.iml.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<module relativePaths="true" type="JAVA_MODULE" version="4">
-  <component name="NewModuleRootManager" inherit-compiler-output="true">
-    <exclude-output/>
-    <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="inheritedJdk"/>
-    <orderEntry type="sourceFolder" forTests="false"/>
-    <output url="file://$MODULE_DIR$/out/production/root"/>
-    <output-test url="file://$MODULE_DIR$/out/test/root"/>
-    <orderEntry type="module-library" exported="" scope="RUNTIME">
-      <library>
-        <CLASSES>
-          <root url="jar://@CACHE_DIR@/commons-collections/commons-collections/jars/commons-collections-3.2.jar!/"/>
-        </CLASSES>
-        <JAVADOC/>
-        <SOURCES>
-          <root url="jar://@CACHE_DIR@/commons-collections/commons-collections/sources/commons-collections-3.2-sources.jar!/"/>
-        </SOURCES>
-      </library>
-    </orderEntry>
-    <orderEntry type="module-library" exported="" scope="RUNTIME">
-      <library>
-        <CLASSES>
-          <root url="jar://@CACHE_DIR@/junit/junit/jars/junit-4.7.jar!/"/>
-        </CLASSES>
-        <JAVADOC/>
-        <SOURCES>
-          <root url="jar://@CACHE_DIR@/junit/junit/sources/junit-4.7-sources.jar!/"/>
-        </SOURCES>
-      </library>
-    </orderEntry>
-  </component>
-</module>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/overwritesExistingDependencies/root.iml b/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/overwritesExistingDependencies/root.iml
deleted file mode 100644
index 55c6433..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/overwritesExistingDependencies/root.iml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<module relativePaths="true" type="JAVA_MODULE" version="4">
-  <component name="NewModuleRootManager" inherit-compiler-output="true">
-    <exclude-output />
-    <content url="file://$MODULE_DIR$" />
-    <orderEntry type="inheritedJdk" />
-    <orderEntry type="sourceFolder" forTests="false" />
-    <orderEntry type="module-library" scope="RUNTIME">
-      <library>
-        <CLASSES>
-          <root url="jar://$CUSTOM_DIR$/../cache/junit/junit/jars/junit-4.7.jar!/"/>
-        </CLASSES>
-        <JAVADOC/>
-        <SOURCES>
-          <root url="jar://$CUSTOM_DIR$/../cache/junit/junit/sources/junit-4.7-sources.jar!/"/>
-        </SOURCES>
-      </library>
-    </orderEntry>
-  </component>
-</module>
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/build.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/build.gradle
deleted file mode 100644
index f8e3ff8..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/build.gradle
+++ /dev/null
@@ -1,4 +0,0 @@
-apply plugin: 'idea'
-project('a') {
-    apply plugin: 'idea'
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/settings.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/settings.gradle
deleted file mode 100644
index eb9dd6f..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/settings.gradle
+++ /dev/null
@@ -1,3 +0,0 @@
-include 'a', 'b'
-
-rootProject.name = 'root'
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithAnEmptyProject/expectedFiles/root.iml.xml b/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithAnEmptyProject/expectedFiles/root.iml.xml
deleted file mode 100644
index bd2993e..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithAnEmptyProject/expectedFiles/root.iml.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<module relativePaths="true" type="JAVA_MODULE" version="4">
-  <component name="NewModuleRootManager">
-    <exclude-output/>
-    <orderEntry type="inheritedJdk"/>
-    <content url="file://$MODULE_DIR$/">
-      <excludeFolder url="file://$MODULE_DIR$/.gradle"/>
-      <excludeFolder url="file://$MODULE_DIR$/build"/>
-    </content>
-    <orderEntry type="sourceFolder" forTests="false"/>
-  </component>
-  <component name="ModuleRootManager"/>
-</module>
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/root/root.iml.xml b/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/root/root.iml.xml
deleted file mode 100644
index 1096372..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/root/root.iml.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<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$/out/production/root"/>
-    <output-test url="file://$MODULE_DIR$/out/test/root"/>
-  </component>
-  <component name="ModuleRootManager"/>
-</module>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/top-level.iml.xml b/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/top-level.iml.xml
deleted file mode 100644
index 2d7dc07..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/top-level.iml.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<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$/out/production/top-level"/>
-    <output-test url="file://$MODULE_DIR$/out/test/top-level"/>
-  </component>
-  <component name="ModuleRootManager"/>
-</module>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSelectedTestsChange/build.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSelectedTestsChange/build.gradle
deleted file mode 100644
index f364acc..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSelectedTestsChange/build.gradle
+++ /dev/null
@@ -1,14 +0,0 @@
-apply plugin: 'java'
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    testCompile 'junit:junit:4.7'
-    testCompile 'org.testng:testng:5.13.1'
-}
-
-test {
-    include '**/*Test.*'
-}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/shared/build.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/shared/build.gradle
deleted file mode 100644
index 145edfd..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/shared/build.gradle
+++ /dev/null
@@ -1,9 +0,0 @@
-apply plugin: 'java'
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    testCompile 'junit:junit:4.7'
-}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/build.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/build.gradle
deleted file mode 100644
index 1efaa24..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/build.gradle
+++ /dev/null
@@ -1,21 +0,0 @@
-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/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canRunJunit3Tests/build.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canRunJunit3Tests/build.gradle
deleted file mode 100644
index ca04df1..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canRunJunit3Tests/build.gradle
+++ /dev/null
@@ -1,9 +0,0 @@
-apply plugin: 'java'
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    testCompile "junit:junit:${junitVersion}"
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canRunJunit3Tests/src/test/java/org/gradle/Test1.java b/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canRunJunit3Tests/src/test/java/org/gradle/Test1.java
deleted file mode 100644
index 5673512..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canRunJunit3Tests/src/test/java/org/gradle/Test1.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.gradle;
-
-import junit.framework.TestCase;
-
-public class Test1 extends TestCase {
-    public void testRenamesItself() {
-        setName("a test that renames itself");
-    }
-}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canRunSingleTests/build.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canRunSingleTests/build.gradle
deleted file mode 100644
index de01156..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canRunSingleTests/build.gradle
+++ /dev/null
@@ -1,9 +0,0 @@
-apply plugin: 'java'
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    testCompile 'junit:junit:4.8.1'
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/detectsTestClasses/build.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/detectsTestClasses/build.gradle
deleted file mode 100644
index 9e3599e..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/detectsTestClasses/build.gradle
+++ /dev/null
@@ -1,8 +0,0 @@
-apply plugin: 'java'
-
-repositories {
-    mavenCentral()
-}
-dependencies {
-    compile 'junit:junit:4.7'
-}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/executesTestsInCorrectEnvironment/build.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/executesTestsInCorrectEnvironment/build.gradle
deleted file mode 100644
index fad6253..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/executesTestsInCorrectEnvironment/build.gradle
+++ /dev/null
@@ -1,8 +0,0 @@
-apply plugin: 'java'
-repositories { mavenCentral() }
-dependencies { testCompile 'junit:junit:4.4', 'ant:ant:1.6.1', 'ant:ant-launcher:1.6.1' }
-test {
-    systemProperties.testSysProperty = 'value'
-    systemProperties.projectDir = projectDir
-    environment.TEST_ENV_VAR = 'value'
-}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OkTest.java b/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OkTest.java
deleted file mode 100644
index 5370bea..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OkTest.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package org.gradle;
-
-import static org.junit.Assert.*;
-
-import java.io.PrintStream;
-import java.util.logging.Logger;
-
-public class OkTest {
-    static {
-        System.out.println("class loaded");
-    }
-
-    public OkTest() {
-        System.out.println("test constructed");
-    }
-
-    @org.junit.Test
-    public void ok() throws Exception {
-        // check JUnit version
-        assertEquals("4.4", new org.junit.runner.JUnitCore().getVersion());
-        // check Ant version
-        assertTrue(org.apache.tools.ant.Main.getAntVersion().contains("1.6.1"));
-        // check working dir
-        assertEquals(System.getProperty("projectDir"), System.getProperty("user.dir"));
-        // check classloader
-        assertSame(ClassLoader.getSystemClassLoader(), getClass().getClassLoader());
-        assertSame(getClass().getClassLoader(), Thread.currentThread().getContextClassLoader());
-        // check Gradle and impl classes not visible
-        try {
-            getClass().getClassLoader().loadClass("org.gradle.api.Project");
-            fail();
-        } catch (ClassNotFoundException e) {
-        }
-        try {
-            getClass().getClassLoader().loadClass("org.slf4j.Logger");
-            fail();
-        } catch (ClassNotFoundException e) {
-        }
-        // check sys properties
-        assertEquals("value", System.getProperty("testSysProperty"));
-        // check env vars
-        assertEquals("value", System.getenv("TEST_ENV_VAR"));
-
-        // check stdout and stderr and logging
-        System.out.println("This is test stdout");
-        System.out.print("no EOL");
-        System.out.println();
-        System.err.println("This is test stderr");
-        Logger.getLogger("test-logger").warning("this is a warning");
-
-        final PrintStream out = System.out;
-        // logging from a shutdown hook
-        Runtime.getRuntime().addShutdownHook(new Thread() {
-            @Override
-            public void run() {
-                out.println("stdout from a shutdown hook.");
-                Logger.getLogger("test-logger").info("info from a shutdown hook.");
-            }
-        });
-
-        // logging from another thread
-        Thread thread = new Thread() {
-            @Override
-            public void run() {
-                System.out.println("stdout from another thread");
-                Logger.getLogger("test-logger").info("info from another thread.");
-            }
-        };
-        thread.start();
-        thread.join();
-    }
-}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/build.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/build.gradle
deleted file mode 100644
index 8ea4a04..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/build.gradle
+++ /dev/null
@@ -1,3 +0,0 @@
-apply plugin: 'java'
-repositories { mavenCentral() }
-dependencies { testCompile 'junit:junit:4.7' }
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/buildSrc/build.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/buildSrc/build.gradle
deleted file mode 100644
index 65d1a05..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/buildSrc/build.gradle
+++ /dev/null
@@ -1,22 +0,0 @@
-println 'buildSrc quiet'
-logger.info 'buildSrc info'
-
-task classes << {}
-
-convention.plugins.java = new ProjectInfo(project: project);
-
-class ProjectInfo implements org.gradle.api.internal.plugins.EmbeddableJavaProject {
-    def Project project
-
-    Collection<String> getRebuildTasks() {
-        return ['classes']
-    }
-
-    Collection<String> getBuildTasks() {
-        return ['classes']
-    }
-
-    FileCollection getRuntimeClasspath() {
-        return project.files()
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/external.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/external.gradle
deleted file mode 100644
index c0f53f0..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/external.gradle
+++ /dev/null
@@ -1,9 +0,0 @@
-println 'external QUIET message'
-logging.captureStandardOutput LogLevel.INFO
-println 'external INFO message'
-
-System.err.println 'external ERROR error message'
-logging.captureStandardError LogLevel.LIFECYCLE
-System.err.println 'external LIFECYCLE error message'
-
-logger.lifecycle 'external LIFECYCLE log message'
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/init.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/init.gradle
deleted file mode 100644
index 3569a94..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/init.gradle
+++ /dev/null
@@ -1,46 +0,0 @@
-
-println 'init QUIET out'
-logging.captureStandardOutput LogLevel.INFO
-println 'init INFO out'
-
-System.err.println 'init ERROR err'
-logging.captureStandardError LogLevel.INFO
-System.err.println 'init INFO err'
-
-logger.lifecycle('init lifecycle log')
-logger.info('init info log')
-
-useLogger(new CustomLogger())
-
-class CustomLogger extends BuildAdapter implements BuildListener, ProjectEvaluationListener, TaskExecutionListener, TaskActionListener {
-    def logger = Logging.getLogger('init-script')
-
-    public void buildFinished(BuildResult result) {
-        logger.info("LOGGER: build finished")
-        println 'init callback quiet out'
-    }
-
-    public void beforeEvaluate(Project project) {
-        logger.lifecycle("LOGGER: evaluating $project.path")
-    }
-
-    public void afterEvaluate(Project project, ProjectState state) {
-        logger.info('LOGGER: evaluated project')
-    }
-
-    public void beforeExecute(Task task) {
-        logger.lifecycle("LOGGER: executing $task.path")
-    }
-
-    public void afterExecute(Task task, TaskState state) {
-        logger.info('LOGGER: executed task')
-    }
-
-    public void beforeActions(Task task) {
-        logger.info('LOGGER: task starting work')
-    }
-
-    public void afterActions(Task task) {
-        logger.info('LOGGER: task completed work')
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/nestedBuild/buildSrc/build.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/nestedBuild/buildSrc/build.gradle
deleted file mode 100644
index 4ff86c7..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/nestedBuild/buildSrc/build.gradle
+++ /dev/null
@@ -1,22 +0,0 @@
-println 'nestedBuild/buildSrc quiet'
-logger.info 'nestedBuild/buildSrc info'
-
-task classes << { }
-
-convention.plugins.java = new ProjectInfo(project: project);
-
-class ProjectInfo implements org.gradle.api.internal.plugins.EmbeddableJavaProject {
-    def Project project
-
-    Collection<String> getRebuildTasks() {
-        return ['classes']
-    }
-
-    Collection<String> getBuildTasks() {
-        return ['classes']
-    }
-
-    FileCollection getRuntimeClasspath() {
-        return project.files()
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ProjectLayoutIntegrationTest/canUseANonStandardBuildDir/build.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/ProjectLayoutIntegrationTest/canUseANonStandardBuildDir/build.gradle
deleted file mode 100644
index d6b40bb..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/ProjectLayoutIntegrationTest/canUseANonStandardBuildDir/build.gradle
+++ /dev/null
@@ -1,13 +0,0 @@
-apply plugin: 'java'
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    testCompile 'junit:junit:4.7'
-}
-
-buildDirName = 'target'
-sourceSets.main.classesDir = new File(buildDir, 'main-classes')
-sourceSets.test.classesDir = new File(buildDir, 'test-classes')
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/two/two.a b/subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/two/two.a
deleted file mode 100644
index 9c2bb0f..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/two/two.a
+++ /dev/null
@@ -1,3 +0,0 @@
-${one}
-${one+1}
-${one+1+1}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithNoMainArtifact/settings.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithNoMainArtifact/settings.gradle
deleted file mode 100644
index 683b273..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithNoMainArtifact/settings.gradle
+++ /dev/null
@@ -1 +0,0 @@
-rootProject.name = 'root'
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/canListenForTestResults/build.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/canListenForTestResults/build.gradle
deleted file mode 100644
index e0d7f49..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/canListenForTestResults/build.gradle
+++ /dev/null
@@ -1,20 +0,0 @@
-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.exception]" }
-}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/executesTestsInCorrectEnvironment/build.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/executesTestsInCorrectEnvironment/build.gradle
deleted file mode 100644
index 897341d..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/executesTestsInCorrectEnvironment/build.gradle
+++ /dev/null
@@ -1,9 +0,0 @@
-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/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Failing/build.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Failing/build.gradle
deleted file mode 100644
index ee4443f..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Failing/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.6"
-
-    testCompile 'org.testng:testng:5.13.1'
-}
-
-test {
-   useTestNG() 
-}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Passing/build.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Passing/build.gradle
deleted file mode 100644
index ee4443f..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Passing/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.6"
-
-    testCompile 'org.testng:testng:5.13.1'
-}
-
-test {
-   useTestNG() 
-}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/build.gradle b/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/build.gradle
deleted file mode 100644
index d5769dc..0000000
--- a/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/build.gradle
+++ /dev/null
@@ -1,15 +0,0 @@
-apply plugin: 'java'
-
-sourceCompatibility=1.5
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    testCompile 'org.testng:testng:5.13.1'
-}
-
-test {
-   useTestNG()
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/BuildExceptionReporter.java b/subprojects/core/src/main/groovy/org/gradle/BuildExceptionReporter.java
index 8c187e5..db95921 100644
--- a/subprojects/core/src/main/groovy/org/gradle/BuildExceptionReporter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/BuildExceptionReporter.java
@@ -40,14 +40,13 @@ import static org.gradle.logging.StyledTextOutput.Style.UserInput;
  * A {@link BuildListener} which reports the build exception, if any.
  */
 public class BuildExceptionReporter extends BuildAdapter {
-    public final BuildClientMetaData clientMetaData;
-
     private enum ExceptionStyle {
-        None, Sanitized, Full
+        NONE, SANITIZED, FULL
     }
 
     private final StyledTextOutputFactory textOutputFactory;
     private final StartParameter startParameter;
+    private final BuildClientMetaData clientMetaData;
 
     public BuildExceptionReporter(StyledTextOutputFactory textOutputFactory, StartParameter startParameter, BuildClientMetaData clientMetaData) {
         this.textOutputFactory = textOutputFactory;
@@ -101,13 +100,13 @@ public class BuildExceptionReporter extends BuildAdapter {
         }
 
         Throwable exception = null;
-        switch (details.exception) {
-            case None:
+        switch (details.exceptionStyle) {
+            case NONE:
                 break;
-            case Sanitized:
+            case SANITIZED:
                 exception = StackTraceUtils.deepSanitize(details.failure);
                 break;
-            case Full:
+            case FULL:
                 exception = details.failure;
                 break;
         }
@@ -124,30 +123,27 @@ public class BuildExceptionReporter extends BuildAdapter {
     public void reportInternalError(FailureDetails details) {
         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;
+
+        if (startParameter.getLogLevel() != LogLevel.DEBUG) {
+            details.resolution.text("Run with ");
+            details.resolution.withStyle(UserInput).format("--%s", LoggingCommandLineConverter.DEBUG_LONG);
+            details.resolution.text(" option to get additional debug info.");
+            details.exceptionStyle = 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
-                        || debug);
-        if (stacktrace) {
-            details.exception = ExceptionStyle.Sanitized;
+        if (startParameter.getShowStacktrace() == StartParameter.ShowStacktrace.ALWAYS || startParameter.getLogLevel() == LogLevel.DEBUG) {
+            details.exceptionStyle = ExceptionStyle.SANITIZED;
         }
-        boolean fullStacktrace = startParameter != null
-                && (startParameter.getShowStacktrace() == StartParameter.ShowStacktrace.ALWAYS_FULL);
-        if (fullStacktrace) {
-            details.exception = ExceptionStyle.Full;
+        if (startParameter.getShowStacktrace() == StartParameter.ShowStacktrace.ALWAYS_FULL) {
+            details.exceptionStyle = ExceptionStyle.FULL;
         }
 
         if (failure instanceof TaskSelectionException) {
             formatTaskSelectionFailure((TaskSelectionException) failure, details);
         } else {
-            formatGenericFailure(failure, debug, stacktrace, fullStacktrace, details);
+            formatGenericFailure(failure, details);
         }
     }
 
@@ -160,30 +156,14 @@ public class BuildExceptionReporter extends BuildAdapter {
         details.resolution.text(" to get a list of available tasks.");
     }
 
-    private void formatGenericFailure(GradleException failure, boolean debug, boolean stacktrace, boolean fullStacktrace,
-                                      FailureDetails details) {
+    private void formatGenericFailure(GradleException failure, FailureDetails details) {
         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.");
-            }
-        }
+
+        fillInFailureResolution(details);
 
         if (failure instanceof LocationAwareException) {
             LocationAwareException scriptException = (LocationAwareException) failure;
+            details.failure = scriptException.getCause();
             if (scriptException.getLocation() != null) {
                 details.location.text(scriptException.getLocation());
             }
@@ -196,6 +176,24 @@ public class BuildExceptionReporter extends BuildAdapter {
         }
     }
 
+    private void fillInFailureResolution(FailureDetails details) {
+        if (details.exceptionStyle == ExceptionStyle.NONE) {
+            details.resolution.text("Run with ");
+            details.resolution.withStyle(UserInput).format("--%s", DefaultCommandLineConverter.STACKTRACE_LONG);
+            details.resolution.text(" option to get the stack trace. ");
+        }
+
+        if (startParameter.getLogLevel() != LogLevel.DEBUG) {
+            details.resolution.text("Run with ");
+            if (startParameter.getLogLevel() != LogLevel.INFO) {
+                details.resolution.withStyle(UserInput).format("--%s", LoggingCommandLineConverter.INFO_LONG);
+                details.resolution.text(" or ");
+            }
+            details.resolution.withStyle(UserInput).format("--%s", LoggingCommandLineConverter.DEBUG_LONG);
+            details.resolution.text(" option to get more log output.");
+        }
+    }
+
     private String getMessage(Throwable throwable) {
         String message = throwable.getMessage();
         if (GUtil.isTrue(message)) {
@@ -205,12 +203,13 @@ public class BuildExceptionReporter extends BuildAdapter {
     }
 
     private static class FailureDetails {
-        private ExceptionStyle exception = ExceptionStyle.None;
-        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;
+        Throwable failure;
+        final RecordingStyledTextOutput summary = new RecordingStyledTextOutput();
+        final RecordingStyledTextOutput details = new RecordingStyledTextOutput();
+        final RecordingStyledTextOutput location = new RecordingStyledTextOutput();
+        final RecordingStyledTextOutput resolution = new RecordingStyledTextOutput();
+
+        ExceptionStyle exceptionStyle = ExceptionStyle.NONE;
 
         public FailureDetails(Throwable failure) {
             this.failure = failure;
diff --git a/subprojects/core/src/main/groovy/org/gradle/GradleLauncher.java b/subprojects/core/src/main/groovy/org/gradle/GradleLauncher.java
index 94475cf..631444d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/GradleLauncher.java
+++ b/subprojects/core/src/main/groovy/org/gradle/GradleLauncher.java
@@ -139,10 +139,15 @@ public abstract class GradleLauncher {
     public abstract void addStandardOutputListener(StandardOutputListener listener);
 
     /**
-     * <p>Adds a {@link StandardOutputListener} to this build instance. The listener is notified of any text written to
-     * standard error by Gradle's logging system
+     * <p>Adds a {@link StandardOutputListener} to this build instance. The listener is notified of any text written to standard error by Gradle's logging system
      *
      * @param listener The listener to add. Has no effect if the listener has already been added.
      */
     public abstract void addStandardErrorListener(StandardOutputListener listener);
+
+    /**
+     * Returns the {@link StartParameter} used by this build instance.
+     * @return The parameter. Never null.
+     */
+    public abstract StartParameter getStartParameter();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/TaskExecutionLogger.java b/subprojects/core/src/main/groovy/org/gradle/TaskExecutionLogger.java
index 2d6a745..3de80b5 100644
--- a/subprojects/core/src/main/groovy/org/gradle/TaskExecutionLogger.java
+++ b/subprojects/core/src/main/groovy/org/gradle/TaskExecutionLogger.java
@@ -20,8 +20,8 @@ import org.gradle.api.Project;
 import org.gradle.api.Task;
 import org.gradle.api.execution.TaskExecutionListener;
 import org.gradle.api.invocation.Gradle;
-import org.gradle.logging.ProgressLogger;
 import org.gradle.api.tasks.TaskState;
+import org.gradle.logging.ProgressLogger;
 import org.gradle.logging.ProgressLoggerFactory;
 
 /**
@@ -37,16 +37,16 @@ public class TaskExecutionLogger implements TaskExecutionListener {
 
     public void beforeExecute(Task task) {
         assert currentTask == null;
-        currentTask = progressLoggerFactory.start(TaskExecutionLogger.class.getName(), getDisplayName(task));
-        currentTask.progress(getDisplayName(task));
+        currentTask = progressLoggerFactory.newOperation(TaskExecutionLogger.class);
+        String displayName = getDisplayName(task);
+        currentTask.setDescription(String.format("Execute %s", displayName));
+        currentTask.setShortDescription(displayName);
+        currentTask.setLoggingHeader(displayName);
+        currentTask.started();
     }
 
     public void afterExecute(Task task, TaskState state) {
-        if (state.getSkipMessage() != null) {
-            currentTask.completed(state.getSkipMessage());
-        } else {
-            currentTask.completed();
-        }
+        currentTask.completed(state.getSkipMessage());
         currentTask = null;
     }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/GradleScriptException.java b/subprojects/core/src/main/groovy/org/gradle/api/GradleScriptException.java
index 6e7c89a..544a249 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/GradleScriptException.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/GradleScriptException.java
@@ -25,11 +25,6 @@ import org.gradle.api.internal.Contextual;
  */
 @Contextual
 public class GradleScriptException extends GradleException {
-    // Required for @Contextual
-    public GradleScriptException() {
-        super();
-    }
-
     public GradleScriptException(String message, Throwable cause) {
         super(message, cause);
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/JavaVersion.java b/subprojects/core/src/main/groovy/org/gradle/api/JavaVersion.java
index 392412e..fb9ba60 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/JavaVersion.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/JavaVersion.java
@@ -22,7 +22,7 @@ import java.util.regex.Matcher;
  * An enumeration of Java versions.
  */
 public enum JavaVersion {
-    VERSION_1_1(false), VERSION_1_2(false), VERSION_1_3(false), VERSION_1_4(false), VERSION_1_5(true), VERSION_1_6(true);
+    VERSION_1_1(false), VERSION_1_2(false), VERSION_1_3(false), VERSION_1_4(false), VERSION_1_5(true), VERSION_1_6(true), VERSION_1_7(true);
 
     private final boolean hasMajorVersion;
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/LocationAwareException.java b/subprojects/core/src/main/groovy/org/gradle/api/LocationAwareException.java
index d6dbbb4..cf41c26 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/LocationAwareException.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/LocationAwareException.java
@@ -15,54 +15,125 @@
  */
 package org.gradle.api;
 
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.internal.MultiCauseException;
 import org.gradle.groovy.scripts.ScriptSource;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
 import java.util.List;
 
 /**
- * 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}.
+ * A {@code LocationAwareException} is an exception which can be annotated with a location in a script.
  */
-public interface LocationAwareException {
+public class LocationAwareException extends GradleException {
+    private final Throwable target;
+    private final ScriptSource source;
+    private final Integer lineNumber;
+
+    public LocationAwareException(Throwable cause, Throwable target, ScriptSource source, Integer lineNumber) {
+        this.source = source;
+        this.lineNumber = lineNumber;
+        this.target = target;
+        initCause(cause);
+    }
+
+    /**
+     * Returns the target exception.
+     *
+     * @return The target exception. Not null
+     */
+    public Throwable getTarget() {
+        return target;
+    }
+
     /**
      * <p>Returns the undecorated message of this exception.</p>
      *
      * @return The undecorated message. May return null.
      */
-    public String getOriginalMessage();
+    public String getOriginalMessage() {
+        return target.getMessage();
+    }
 
     /**
      * <p>Returns the source of the script where this exception occurred.</p>
      *
      * @return The source. May return null.
      */
-    public ScriptSource getScriptSource();
+    public ScriptSource getScriptSource() {
+        return source;
+    }
 
     /**
      * <p>Returns a description of the location of where this exception occurred.</p>
      *
      * @return The location description. May return null.
      */
-    public String getLocation();
+    public String getLocation() {
+        if (source == null) {
+            return null;
+        }
+        String sourceMsg = StringUtils.capitalize(source.getDisplayName());
+        if (lineNumber == null) {
+            return sourceMsg;
+        }
+        return String.format("%s line: %d", sourceMsg, lineNumber);
+    }
 
     /**
      * Returns the line in the script where this exception occurred, if known.
      *
      * @return The line number, or null if not known.
      */
-    public Integer getLineNumber();
+    public Integer getLineNumber() {
+        return lineNumber;
+    }
 
     /**
      * Returns the fully formatted error message, including the location.
      *
      * @return the message. May return null.
      */
-    public String getMessage();
+    public String getMessage() {
+        String location = getLocation();
+        String message = target.getMessage();
+        if (location == null && message == null) {
+            return null;
+        }
+        if (location == null) {
+            return message;
+        }
+        if (message == null) {
+            return location;
+        }
+        return String.format("%s%n%s", location, message);
+    }
 
     /**
      * Returns the reportable causes for this failure.
      *
      * @return The causes. Never returns null, returns an empty list if this exception has no reportable causes.
      */
-    public List<Throwable> getReportableCauses();
+    public List<Throwable> getReportableCauses() {
+        List<Throwable> causes = new ArrayList<Throwable>();
+        LinkedList<Throwable> queue = new LinkedList<Throwable>();
+        addCauses(target, queue);
+        while (!queue.isEmpty()) {
+            Throwable t = queue.removeFirst();
+            causes.add(t);
+            addCauses(t, queue);
+        }
+        return causes;
+    }
+
+    private void addCauses(Throwable t, Collection<Throwable> causes) {
+        if (t instanceof MultiCauseException) {
+            MultiCauseException multiCauseException = (MultiCauseException) t;
+            causes.addAll(multiCauseException.getCauses());
+        } else if (t.getCause() != null) {
+            causes.add(t.getCause());
+        }
+    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/Project.java b/subprojects/core/src/main/groovy/org/gradle/api/Project.java
index f2a9dcf..7a21a64 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/Project.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/Project.java
@@ -445,6 +445,12 @@ public interface Project extends Comparable<Project> {
      * <tr><td><code>{@value org.gradle.api.Task#TASK_ACTION}</code></td><td>A closure or {@link Action} to add to the
      * task.</td><td><code>null</code></td></tr>
      *
+     * <tr><td><code>{@value org.gradle.api.Task#TASK_DESCRIPTION}</code></td><td>A description of the task.
+     * </td><td><code>null</code></td></tr>
+     *
+     * <tr><td><code>{@value org.gradle.api.Task#TASK_GROUP}</code></td><td>A task group which this task belongs to.
+     * </td><td><code>null</code></td></tr>
+     *
      * </table>
      *
      * <p>After the task is added to the project, it is made available as a property of the project, so that you can
@@ -819,15 +825,15 @@ public interface Project extends Comparable<Project> {
      * <p>Returns a {@link ConfigurableFileCollection} containing the given files. You can pass any of the following
      * types to this method:</p>
      *
-     * <ul> <li>A {@code String}. Interpreted relative to the project directory, as for {@link #file(Object)}. A string
+     * <ul> <li>A {@link String}. Interpreted relative to the project directory, as for {@link #file(Object)}. A string
      * that starts with {@code file:} is treated as a file URL.</li>
      *
-     * <li>A {@code File}. Interpreted relative to the project directory, as for {@link #file(Object)}.</li>
+     * <li>A {@link File}. Interpreted relative to the project directory, as for {@link #file(Object)}.</li>
      *
      * <li>{@link java.net.URI} or {@link java.net.URL}. The URL's path is interpreted as a file path. Currently, only
      * {@code file:} URLs are supported.
      *
-     * <li>A {@code Collection} or an array. May contain any of the types listed here. The elements of the collection
+     * <li>A {@link java.util.Collection}, {@link Iterable}, or an array. May contain any of the types listed here. The elements of the collection
      * are recursively converted to files.</li>
      *
      * <li>A {@link org.gradle.api.file.FileCollection}. The contents of the collection are included in the returned
@@ -1468,7 +1474,7 @@ public interface Project extends Comparable<Project> {
      * @param <T> The type of objects for the container to contain.
      * @return The container.
      */
-    <T> NamedDomainObjectContainer<T> container(Class<T> type, NamedDomainObjectFactory<? extends T> factory);
+    <T> NamedDomainObjectContainer<T> container(Class<T> type, NamedDomainObjectFactory<T> factory);
 
     /**
      * Creates a container for managing named objects of the specified type. The given closure is used to create object instances. The name of the instance to be created is passed as a parameter to
@@ -1480,4 +1486,4 @@ public interface Project extends Comparable<Project> {
      * @return The container.
      */
     <T> NamedDomainObjectContainer<T> container(Class<T> type, Closure factoryClosure);
-}
\ No newline at end of file
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/Task.java b/subprojects/core/src/main/groovy/org/gradle/api/Task.java
index 0cadca3..6f6aecd 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/Task.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/Task.java
@@ -134,6 +134,8 @@ public interface Task extends Comparable<Task> {
 
     public static final String TASK_DESCRIPTION = "description";
 
+    public static final String TASK_GROUP = "group";
+
     public static final String TASK_TYPE = "type";
 
     public static final String TASK_DEPENDS_ON = "dependsOn";
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/Configuration.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/Configuration.java
index a71c005..f70552f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/Configuration.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/Configuration.java
@@ -169,7 +169,7 @@ public interface Configuration extends FileCollection {
     Set<File> files(Dependency... dependencies);
 
     /**
-     * Resolves this configuration lazyly. The resolve happens when the elements of the returned FileCollection get accessed the first time.
+     * Resolves this configuration lazily. The resolve happens when the elements of the returned FileCollection get accessed the first time.
      * This locates and downloads the files which make up this configuration. Only the resulting set of files belonging to the subset
      * of dependencies specified by the dependencySpec is contained in the FileCollection.
      *
@@ -188,7 +188,7 @@ public interface Configuration extends FileCollection {
     FileCollection fileCollection(Closure dependencySpecClosure);
 
     /**
-     * Resolves this configuration lazyly. The resolve happens when the elements of the returned FileCollection get accessed the first time.
+     * Resolves this configuration lazily. The resolve happens when the elements of the returned FileCollection get accessed the first time.
      * This locates and downloads the files which make up this configuration. Only the resulting set of files belonging to specified
      * dependencies is contained in the FileCollection.
      *
@@ -225,10 +225,10 @@ public interface Configuration extends FileCollection {
     /**
      * Returns a TaskDependency object containing dependencies on all tasks with the specified name from project
      * dependencies related to this configuration or one of its super configurations.  These other projects may be
-     * projects this configuration depends on or projects with a similarly named configuation that depend on this one
+     * projects this configuration depends on or projects with a similarly named configuration that depend on this one
      * based on the useDependOn argument.
      *
-     * @param useDependedOn if true, add tasks from project dependencies in this conifguration, otherwise use projects
+     * @param useDependedOn if true, add tasks from project dependencies in this configuration, otherwise use projects
      *                      from configurations with the same name that depend on this one.
      * @param taskName name of task to depend on
      * @return the populated TaskDependency object
@@ -253,8 +253,9 @@ public interface Configuration extends FileCollection {
      *
      * @see org.gradle.api.tasks.Upload
      * @see #getUploadTaskName()
+     * @throws PublishException On failure to publish this configuration.
      */
-    void publish(List<DependencyResolver> publishRepositories, File descriptorDestination);
+    void publish(List<DependencyResolver> publishRepositories, File descriptorDestination) throws PublishException;
 
     /**
      * Gets the set of dependencies directly contained in this configuration
@@ -373,9 +374,9 @@ public interface Configuration extends FileCollection {
 
     /**
      * Creates a copy of this configuration ignoring superconfigurations (see {@link #copy()} but filtering
-     * the dependencies using the dependencySpec.  The dependencySpec may be obtained from
-     * {@link org.gradle.api.artifacts.specs.DependencySpecs DependencySpecs.type()} like
-     * DependencySpecs.type(Type.EXTERNAL)
+     * the dependencies using the specified dependency spec. {@link org.gradle.api.artifacts.specs.Type}
+     * provides some predefined dependency specs.
+     *
      * @param dependencySpec filtering requirements
      * @return copy of this configuration
      */
@@ -383,9 +384,9 @@ public interface Configuration extends FileCollection {
 
     /**
      * Creates a copy of this configuration with dependencies from superconfigurations (see {@link #copyRecursive()})
-     *  but filtering the dependencies using the dependencySpec.  The dependencySpec may be obtained from
-     * {@link org.gradle.api.artifacts.specs.DependencySpecs DependencySpecs.type()} like
-     * DependencySpecs.type(Type.EXTERNAL)
+     * but filtering the dependencies using the dependencySpec. {@link org.gradle.api.artifacts.specs.Type}
+     * provides some predefined dependency specs.
+     *
      * @param dependencySpec filtering requirements
      * @return copy of this configuration
      */
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/PublishException.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/PublishException.java
new file mode 100644
index 0000000..e37998e
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/PublishException.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.artifacts;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.internal.Contextual;
+
+/**
+ * <p>A <code>PublishException</code> is thrown when a dependency configuration cannot be published for some reason.</p>
+ */
+ at Contextual
+public class PublishException extends GradleException {
+    public PublishException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ResolveException.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ResolveException.java
index 9b1c0dc..0e3ad6d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ResolveException.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ResolveException.java
@@ -27,10 +27,6 @@ import org.gradle.util.GUtil;
  */
 @Contextual
 public class ResolveException extends GradleException {
-    // Required for @Contextual
-    public ResolveException() {
-    }
-
     public ResolveException(Configuration configuration, String message) {
         super(buildMessage(configuration, message));
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ResolvedConfiguration.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ResolvedConfiguration.java
index 12fa35b..0a14e53 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ResolvedConfiguration.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ResolvedConfiguration.java
@@ -59,6 +59,17 @@ public interface ResolvedConfiguration {
     Set<ResolvedDependency> getFirstLevelModuleDependencies() throws ResolveException;
 
     /**
+     * Returns the {@link ResolvedDependency} instances for each direct dependency of the configuration that matches
+     * the given spec. Via those you have access to all {@link ResolvedDependency} instances, including the transitive
+     * dependencies of the configuration.
+     *
+     * @param dependencySpec A filter for the dependencies to be resolved.
+     * @return A {@code ResolvedDependency} instance for each direct dependency.
+     * @throws ResolveException when the resolve was not successful.
+     */
+    Set<ResolvedDependency> getFirstLevelModuleDependencies(Spec<Dependency> dependencySpec) throws ResolveException;
+
+    /**
      * Returns the set of artifact meta-data for this configuration.
      *
      * @return The set of artifacts.
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ResolverContainer.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ResolverContainer.java
index 2b7bdee..0d017ad 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ResolverContainer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ResolverContainer.java
@@ -57,7 +57,6 @@ public interface ResolverContainer extends NamedDomainObjectContainer<Dependency
     String DEFAULT_MAVEN_LOCAL_REPO_NAME = "MavenLocal";
     String MAVEN_CENTRAL_URL = "http://repo1.maven.org/maven2/";
     String MAVEN_REPO_PATTERN = "[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]";
-    String FLAT_DIR_RESOLVER_PATTERN = "[artifact](-[revision])(-[classifier]).[ext]";
     String DEFAULT_CACHE_ARTIFACT_PATTERN
             = "[organisation]/[module](/[branch])/[type]s/[artifact]-[revision](-[classifier])(.[ext])";
     String DEFAULT_CACHE_IVY_PATTERN = "[organisation]/[module](/[branch])/ivy-[revision].xml";
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/dsl/ArtifactRepository.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/dsl/ArtifactRepository.java
new file mode 100644
index 0000000..42e2eaa
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/dsl/ArtifactRepository.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+/**
+ * A repository for resolving and publishing artifacts.
+ */
+public interface ArtifactRepository {
+    /**
+     * Returns the name for this repository.
+     *
+     * @return The name.
+     */
+    String getName();
+
+    /**
+     * Sets the name for this repository.
+     *
+     * @param name The name. Must not be null.
+     */
+    void setName(String name);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/dsl/IvyArtifactRepository.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/dsl/IvyArtifactRepository.java
new file mode 100644
index 0000000..527e3aa
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/dsl/IvyArtifactRepository.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+/**
+ * An artifact repository which uses an Ivy format to store artifacts and meta-data.
+ */
+public interface IvyArtifactRepository extends ArtifactRepository {
+    /**
+     * Returns the user name to use when authenticating to this repository.
+     *
+     * @return The user name. May be null.
+     */
+    String getUserName();
+
+    /**
+     * Sets the user name to use when authenticating to this repository.
+     *
+     * @param username The user name. May be null.
+     */
+    void setUserName(String username);
+
+    /**
+     * Returns the password to use when authenticating to this repository.
+     *
+     * @return The password. May be null.
+     */
+    String getPassword();
+
+    /**
+     * Sets the password to use when authenticating to this repository.
+     *
+     * @param password The password. May be null.
+     */
+    void setPassword(String password);
+
+    /**
+     * Adds an Ivy artifact pattern to use to locate artifacts in this repository.
+     *
+     * @param pattern The artifact pattern.
+     */
+    void artifactPattern(String pattern);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/dsl/RepositoryHandler.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/dsl/RepositoryHandler.java
index b230e30..61315ba 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/dsl/RepositoryHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/dsl/RepositoryHandler.java
@@ -18,6 +18,7 @@ package org.gradle.api.artifacts.dsl;
 import groovy.lang.Closure;
 import org.apache.ivy.plugins.resolver.DependencyResolver;
 import org.apache.ivy.plugins.resolver.FileSystemResolver;
+import org.gradle.api.Action;
 import org.gradle.api.artifacts.ResolverContainer;
 import org.gradle.api.artifacts.maven.GroovyMavenDeployer;
 import org.gradle.api.artifacts.maven.MavenResolver;
@@ -249,13 +250,26 @@ public interface RepositoryHandler extends ResolverContainer, ResolverProvider {
     MavenResolver mavenInstaller(Map<String, ?> args);
 
     /**
-     * Behaves the same way as {@link #mavenInstaller(java.util.Map)}. Additionally a closure can be passed to
-     * further configure the added repository.
+     * Behaves the same way as {@link #mavenInstaller(java.util.Map)}. Additionally a closure can be passed to further configure the added repository.
      *
      * @param args The argument to create the repository
-     * @param configureClosure
      * @return The added repository
      */
     MavenResolver mavenInstaller(Map<String, ?> args, Closure configureClosure);
 
+    /**
+     * Adds and configures an Ivy repository.
+     *
+     * @param closure The closure to use to configure the repository.
+     * @return The added repository.
+     */
+    IvyArtifactRepository ivy(Closure closure);
+
+    /**
+     * Adds and configures an Ivy repository.
+     *
+     * @param action The action to use to configure the repository.
+     * @return The added repository.
+     */
+    IvyArtifactRepository ivy(Action<? super IvyArtifactRepository> action);
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/maven/MavenDeployer.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/maven/MavenDeployer.java
index 038c990..0c7a609 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/maven/MavenDeployer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/maven/MavenDeployer.java
@@ -15,8 +15,6 @@
  */
 package org.gradle.api.artifacts.maven;
 
-import org.apache.maven.artifact.ant.RemoteRepository;
-
 import java.io.File;
 import java.util.Collection;
 
@@ -41,9 +39,9 @@ public interface MavenDeployer extends MavenResolver {
     /**
      * Returns the repository o be used for uploading artifacts.
      *
-     * @see #setRepository(org.apache.maven.artifact.ant.RemoteRepository)
+     * @see #setRepository(Object)
      */
-    RemoteRepository getRepository();
+    Object getRepository();
 
     /**
      * Sets the repository to be used for uploading artifacts. If {@link #getRepository()} is not set, this repository
@@ -51,14 +49,14 @@ public interface MavenDeployer extends MavenResolver {
      *
      * @param repository The repository to be used
      */
-    void setRepository(RemoteRepository repository);
+    void setRepository(Object repository);
 
     /**
      * Returns the repository o be used for uploading snapshot artifacts.
      *
-     * @see #setSnapshotRepository(org.apache.maven.artifact.ant.RemoteRepository)
+     * @see #setSnapshotRepository(Object)
      */
-    RemoteRepository getSnapshotRepository();
+    Object getSnapshotRepository();
 
     /**
      * Sets the repository to be used for uploading snapshot artifacts. If this is not set, the {@link #getRepository()}
@@ -66,7 +64,7 @@ public interface MavenDeployer extends MavenResolver {
      *
      * @param snapshotRepository The repository to be used
      */
-    void setSnapshotRepository(RemoteRepository snapshotRepository);
+    void setSnapshotRepository(Object snapshotRepository);
 
     /**
      * Out of the box only uploading to the filesysten and via http is supported. If other protocolls should be used,
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/maven/MavenDeployment.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/maven/MavenDeployment.java
index 89b415a..8bddc3c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/maven/MavenDeployment.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/maven/MavenDeployment.java
@@ -51,4 +51,11 @@ public interface MavenDeployment {
      * @param artifact The artifact to add.
      */
     void addArtifact(PublishArtifact artifact);
+
+    /**
+     * Returns the additional artifacts for this deployment.
+     *
+     * @return the additional artifacts for this deployment. Never null.
+     */
+    public Set<PublishArtifact> getAttachedArtifacts();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/maven/MavenFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/maven/MavenFactory.java
new file mode 100644
index 0000000..07444a7
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/maven/MavenFactory.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.ConfigurationContainer;
+import org.gradle.api.internal.Factory;
+import org.gradle.api.internal.file.FileResolver;
+
+import java.util.Map;
+
+/**
+ * Factory for various types related to Maven dependency management.
+ */
+public interface MavenFactory {
+    Factory<MavenPom> createMavenPomFactory(ConfigurationContainer configurationContainer, Map<Configuration, Conf2ScopeMapping> mappings, FileResolver fileResolver);
+
+    Factory<MavenPom> createMavenPomFactory(ConfigurationContainer configurationContainer, Conf2ScopeMappingContainer conf2ScopeMappingContainer, FileResolver fileResolver);
+
+    Conf2ScopeMappingContainer createConf2ScopeMappingContainer(Map<Configuration, Conf2ScopeMapping> mappings);
+}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/maven/MavenPom.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/maven/MavenPom.java
index 0018e53..61154b9 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/maven/MavenPom.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/maven/MavenPom.java
@@ -16,8 +16,6 @@
 package org.gradle.api.artifacts.maven;
 
 import groovy.lang.Closure;
-import org.apache.maven.model.Dependency;
-import org.apache.maven.model.Model;
 import org.gradle.api.Action;
 import org.gradle.api.artifacts.ConfigurationContainer;
 
@@ -124,14 +122,14 @@ public interface MavenPom {
      * @see org.apache.maven.model.Model#setDependencies(java.util.List)
      * @return this
      */
-    MavenPom setDependencies(List<Dependency> dependencies);
+    MavenPom setDependencies(List<?> dependencies);
 
     /**
      * Returns the dependencies for this POM.
      * 
      * @see org.apache.maven.model.Model#getDependencies()
      */
-    List<Dependency> getDependencies();
+    List<?> getDependencies();
 
     /**
      * Returns the underlying native Maven {@link org.apache.maven.model.Model} object. The MavenPom object
@@ -141,7 +139,7 @@ public interface MavenPom {
      *
      * @return the underlying native Maven object
      */
-    Model getModel();
+    Object getModel();
 
     /**
      * Sets the underlying native Maven {@link org.apache.maven.model.Model} object.
@@ -150,7 +148,7 @@ public interface MavenPom {
      * @return this
      * @see #getModel() 
      */
-    MavenPom setModel(Model model);
+    MavenPom setModel(Object model);
 
     /**
      * Writes the {@link #getEffectivePom()} xml to a writer while applying the {@link #withXml(org.gradle.api.Action)} actions.
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/maven/MavenResolver.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/maven/MavenResolver.java
index 12268fe..63f9b50 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/maven/MavenResolver.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/maven/MavenResolver.java
@@ -17,7 +17,6 @@ package org.gradle.api.artifacts.maven;
 
 import groovy.lang.Closure;
 import org.apache.ivy.plugins.resolver.DependencyResolver;
-import org.apache.maven.settings.Settings;
 import org.gradle.api.Action;
 
 /**
@@ -30,7 +29,7 @@ public interface MavenResolver extends DependencyResolver, PomFilterContainer {
      * Returns a maven settings object. This can be used for example to figure out where the local repository is
      * located. This property is filled after publishing. Before this property is null.
      */
-    Settings getSettings();
+    Object getSettings();
 
     /**
      * Adds an action to be executed immediately before a deployment to this resolver. The action is executed after all
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/specs/DependencySpecs.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/specs/DependencySpecs.java
index eee91da..ba80997 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/specs/DependencySpecs.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/specs/DependencySpecs.java
@@ -24,6 +24,10 @@ import org.gradle.api.specs.Spec;
  * @author Hans Dockter
  */
 public class DependencySpecs {
+    /**
+     * Deprecated. Use {@link Type} directly instead (it already implements Spec<Dependency>).
+     */
+    @Deprecated
     public static Spec<Dependency> type(Type type) {
         return new DependencyTypeSpec<Dependency>(type);
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/specs/Type.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/specs/Type.java
index c4edf5b..d62f5e8 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/specs/Type.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/specs/Type.java
@@ -19,21 +19,33 @@ package org.gradle.api.artifacts.specs;
 import org.gradle.api.artifacts.Dependency;
 import org.gradle.api.artifacts.ProjectDependency;
 import org.gradle.api.artifacts.ExternalDependency;
+import org.gradle.api.specs.Spec;
 
 /**
  * Dependency types.
  */
-public enum Type {
+public enum Type implements Spec<Dependency> {
     EXTERNAL {
-        public boolean isOf(Dependency dependency) {
+        public boolean isSatisfiedBy(Dependency dependency) {
             return dependency instanceof ExternalDependency;
         }
+        public boolean isOf(Dependency dependency) {
+            return isSatisfiedBy(dependency);
+        }
     },
     PROJECT {
-        public boolean isOf(Dependency dependency) {
+        public boolean isSatisfiedBy(Dependency dependency) {
             return dependency instanceof ProjectDependency;
         }
+        public boolean isOf(Dependency dependency) {
+            return isSatisfiedBy(dependency);
+        }
     };
 
+
+    /**
+     * Deprecated. Use isSatisfiedBy() instead.
+     */
+    @Deprecated
     public abstract boolean isOf(Dependency dependency);
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/dsl/ConvenienceProperty.java b/subprojects/core/src/main/groovy/org/gradle/api/dsl/ConvenienceProperty.java
new file mode 100644
index 0000000..c21e4b0
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/dsl/ConvenienceProperty.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.api.dsl;
+
+/**
+ * ConventionProperty can be assigned but <b>cannot</b> be mutated (even if the object is mutable!)
+ * <p>
+ * Understanding convention properties is important mostly for collections
+ * because one might want to mutate the collection but it wouldn't work.
+ * <p>
+ * Consider this example:
+ *
+ * <pre>
+ * someTask {
+ *   //Convention properties cannot be mutated,
+ *   //even if the object is mutable!
+ *   conventionProperty.add('c')  //WRONG!
+ *
+ *   //However, convention properties can be assigned:
+ *   conventionProperty += 'c'  //OK
+ *   conventionProperty = ['a', 'b']  //OK
+ *
+ *   //Simple properties can be mutated or assigned:
+ *   simpleProperty = ['1.5']  //OK
+ *   simpleProperty.add('1.5')  //OK
+ * }
+ * </pre>
+ *
+ * You may wonder why Gradle uses convention properties.
+ * The reason for that is that internally, convention properties are evaluated 'lazily'.
+ * This means that Gradle can configure tasks and objects with reasonable defaults
+ * without worrying about the order of statements that configure the build. Example:
+ *
+ * <pre>
+ * apply plugin: 'java'
+ *
+ * test {
+ *   //test task has a testClassesDir convention property
+ *   //that is by default configured to 'test classes dir'
+ *   //testClassesDir = sourceSets.test.classesDir
+ * }
+ *
+ * //what if someone reconfigured the 'test classes dir'
+ * //after the 'test' task was configured? Like that:
+ * sourceSets.test.classesDir = new File(buildDir, 'test-classes')
+ *
+ * //will the already-configured test.testClassesDir property
+ * //on a 'test' task point to a wrong folder?
+ * </pre>
+ *
+ * Answer: It will all work fine!
+ * <p>
+ * Thanks to the 'lazy' evaluation of the convention properties
+ * the user can reconfigure the sourceSets anywhere in the gradle script -
+ * and still the test.testClassesDir will point to the right folder.
+ *
+ * Author: Szczepan Faber, created at: 4/19/11
+ */
+public class ConvenienceProperty {}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/dsl/package-info.java b/subprojects/core/src/main/groovy/org/gradle/api/dsl/package-info.java
new file mode 100644
index 0000000..3c59c30
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/dsl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * See docs for {@link PropertyKinds}
+ */
+package org.gradle.api.dsl;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/file/ConfigurableFileCollection.java b/subprojects/core/src/main/groovy/org/gradle/api/file/ConfigurableFileCollection.java
index f359320..9389957 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/file/ConfigurableFileCollection.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/file/ConfigurableFileCollection.java
@@ -25,8 +25,28 @@ import java.util.Set;
  */
 public interface ConfigurableFileCollection extends FileCollection {
     /**
-     * Adds a set of files to this collection. The given paths are evaluated as for {@link
-     * org.gradle.api.Project#files(Object...)}.
+     * Returns the set of source paths for this collection. The paths are evaluated as for {@link org.gradle.api.Project#files(Object...)}.
+     *
+     * @return The set of source paths. Returns an empty set if none.
+     */
+    Set<Object> getFrom();
+
+    /**
+     * Sets the source paths for this collection. The given paths are evaluated as for {@link org.gradle.api.Project#files(Object...)}.
+     *
+     * @param paths The paths.
+     */
+    void setFrom(Iterable<?> paths);
+
+    /**
+     * Sets the source paths for this collection. The given paths are evaluated as for {@link org.gradle.api.Project#files(Object...)}.
+     *
+     * @param paths The paths.
+     */
+    void setFrom(Object... paths);
+
+    /**
+     * Adds a set of source paths to this collection. The given paths are evaluated as for {@link org.gradle.api.Project#files(Object...)}.
      *
      * @param paths The files to add.
      * @return this
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/file/ConfigurableFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/file/ConfigurableFileTree.java
index 91775e9..173b105 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/file/ConfigurableFileTree.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/file/ConfigurableFileTree.java
@@ -26,7 +26,7 @@ import java.util.Set;
  *
  * <p>You can obtain a {@code ConfigurableFileTree} instance by calling {@link org.gradle.api.Project#fileTree(java.util.Map)}.</p>
  */
-public interface ConfigurableFileTree extends FileTree, PatternFilterable, Buildable {
+public interface ConfigurableFileTree extends FileTree, DirectoryTree, PatternFilterable, Buildable {
     /**
      * Specifies base directory for this file tree using the given path. The path is evaluated as for {@link
      * org.gradle.api.Project#file(Object)}.
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/file/CopySpec.java b/subprojects/core/src/main/groovy/org/gradle/api/file/CopySpec.java
index 7af8266..8b3c497 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/file/CopySpec.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/file/CopySpec.java
@@ -1,236 +1,257 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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 groovy.lang.Closure;
-import org.gradle.api.Action;
-import org.gradle.api.tasks.util.PatternFilterable;
-import org.gradle.api.specs.Spec;
-
-import java.util.Map;
-import java.io.FilterReader;
-import java.util.regex.Pattern;
-
-/**
- * A set of specifications for copying files.  This includes:
- *
- * <ul>
- *
- * <li>source directories (multiples allowed)
- *
- * <li>destination directory
- *
- * <li>ANT like include patterns
- *
- * <li>ANT like exclude patterns
- *
- * <li>File relocating rules
- *
- * <li>renaming rules
- *
- * <li>content filters
- *
- * </ul>
- *
- * CopySpecs may be nested by passing a closure to one of the from methods.  The closure creates a child CopySpec and
- * delegates methods in the closure to the child. Child CopySpecs inherit any values specified in the parent. This
- * allows constructs like:
- * <pre>
- * into('webroot')
- * exclude('**/.svn/**')
- * from('src/main/webapp') {
- *    include '**/*.jsp'
- * }
- * from('src/main/js') {
- *    include '**/*.js'
- * }
- * </pre>
- *
- * In this example, the <code>into</code> and <code>exclude</code> specifications at the root level are inherited by the
- * two child CopySpecs.
- *
- * @author Steve Appling
- * @see org.gradle.api.tasks.Copy Copy Task
- * @see org.gradle.api.Project#copy(groovy.lang.Closure) Project.copy()
- */
-public interface CopySpec extends CopySourceSpec, CopyProcessingSpec, PatternFilterable {
-    /**
-     * Specifies whether case-sensitive pattern matching should be used.
-     *
-     * @return true for case-sensitive matching.
-     */
-    boolean isCaseSensitive();
-
-    /**
-     * Specifies whether case-sensitive pattern matching should be used for this CopySpec.
-     *
-     * @param caseSensitive true for case-sensitive matching.
-     */
-    void setCaseSensitive(boolean caseSensitive);
-
-    /**
-     * Adds the given specs as a child of this spec.
-     * @param sourceSpecs The specs to add
-     * @return this
-     */
-    CopySpec with(CopySpec... sourceSpecs);
-
-    // CopySourceSpec overrides to broaden return type
-
-    /**
-     * {@inheritDoc}
-     */
-    CopySpec from(Object... sourcePaths);
-
-    /**
-     * {@inheritDoc}
-     */
-    CopySpec from(Object sourcePath, Closure c);
-
-    // PatternFilterable overrides to broaden return type
-
-    /**
-     * {@inheritDoc}
-     *
-     * @see org.gradle.api.tasks.util.PatternFilterable Pattern Format
-     */
-    CopySpec setIncludes(Iterable<String> includes);
-
-    /**
-     * {@inheritDoc}
-     *
-     * @see org.gradle.api.tasks.util.PatternFilterable Pattern Format
-     */
-    CopySpec setExcludes(Iterable<String> excludes);
-
-    /**
-     * {@inheritDoc}
-     *
-     * @see org.gradle.api.tasks.util.PatternFilterable Pattern Format
-     */
-    CopySpec include(String... includes);
-
-    /**
-     * {@inheritDoc}
-     *
-     * @see org.gradle.api.tasks.util.PatternFilterable Pattern Format
-     */
-    CopySpec include(Iterable<String> includes);
-
-    /**
-     * {@inheritDoc}
-     *
-     * @see org.gradle.api.tasks.util.PatternFilterable Pattern Format
-     */
-    CopySpec include(Spec<FileTreeElement> includeSpec);
-
-    /**
-     * {@inheritDoc}
-     *
-     * @see org.gradle.api.tasks.util.PatternFilterable Pattern Format
-     */
-    CopySpec include(Closure includeSpec);
-
-    /**
-     * {@inheritDoc}
-     *
-     * @see org.gradle.api.tasks.util.PatternFilterable Pattern Format
-     */
-    CopySpec exclude(String... excludes);
-
-    /**
-     * {@inheritDoc}
-     *
-     * @see org.gradle.api.tasks.util.PatternFilterable Pattern Format
-     */
-    CopySpec exclude(Iterable<String> excludes);
-
-    /**
-     * {@inheritDoc}
-     *
-     * @see org.gradle.api.tasks.util.PatternFilterable Pattern Format
-     */
-    CopySpec exclude(Spec<FileTreeElement> excludeSpec);
-
-    /**
-     * {@inheritDoc}
-     *
-     * @see org.gradle.api.tasks.util.PatternFilterable Pattern Format
-     */
-    CopySpec exclude(Closure excludeSpec);
-
-    // CopyProcessingSpec overrides to broaden return type
-
-    /**
-     * {@inheritDoc}
-     */
-    CopySpec into(Object destPath);
-
-    /**
-     * Creates and configures a child {@code CopySpec} with the given destination path. The destination is evaluated as
-     * for {@link org.gradle.api.Project#file(Object)}.
-     *
-     * @param destPath Path to the destination directory for a Copy
-     * @param configureClosure The closure to use to configure the child {@code CopySpec}.
-     * @return this
-     */
-    CopySpec into(Object destPath, Closure configureClosure);
-
-    /**
-     * {@inheritDoc}
-     */
-    CopySpec rename(Closure closure);
-
-    /**
-     * {@inheritDoc}
-     */
-    CopySpec rename(String sourceRegEx, String replaceWith);
-
-    /**
-     * {@inheritDoc}
-     */
-    CopyProcessingSpec rename(Pattern sourceRegEx, String replaceWith);
-
-    /**
-     * {@inheritDoc}
-     */
-    CopySpec filter(Map<String, ?> properties, Class<? extends FilterReader> filterType);
-
-    /**
-     * {@inheritDoc}
-     */
-    CopySpec filter(Class<? extends FilterReader> filterType);
-
-    /**
-     * {@inheritDoc}
-     */
-    CopySpec filter(Closure closure);
-
-    /**
-     * {@inheritDoc}
-     */
-    CopySpec expand(Map<String, ?> properties);
-
-    /**
-     * {@inheritDoc}
-     */
-    CopySpec eachFile(Action<? super FileCopyDetails> action);
-
-    /**
-     * {@inheritDoc}
-     */
-    CopySpec eachFile(Closure closure);
-}
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 groovy.lang.Closure;
+import org.gradle.api.Action;
+import org.gradle.api.tasks.util.PatternFilterable;
+import org.gradle.api.specs.Spec;
+
+import java.util.Map;
+import java.io.FilterReader;
+import java.util.regex.Pattern;
+
+/**
+ * A set of specifications for copying files.  This includes:
+ *
+ * <ul>
+ *
+ * <li>source directories (multiples allowed)
+ *
+ * <li>destination directory
+ *
+ * <li>ANT like include patterns
+ *
+ * <li>ANT like exclude patterns
+ *
+ * <li>File relocating rules
+ *
+ * <li>renaming rules
+ *
+ * <li>content filters
+ *
+ * </ul>
+ *
+ * CopySpecs may be nested by passing a closure to one of the from methods.  The closure creates a child CopySpec and
+ * delegates methods in the closure to the child. Child CopySpecs inherit any values specified in the parent. This
+ * allows constructs like:
+ * <pre>
+ * into('webroot')
+ * exclude('**/.svn/**')
+ * from('src/main/webapp') {
+ *    include '**/*.jsp'
+ * }
+ * from('src/main/js') {
+ *    include '**/*.js'
+ * }
+ * </pre>
+ *
+ * In this example, the <code>into</code> and <code>exclude</code> specifications at the root level are inherited by the
+ * two child CopySpecs.
+ *
+ * @author Steve Appling
+ * @see org.gradle.api.tasks.Copy Copy Task
+ * @see org.gradle.api.Project#copy(groovy.lang.Closure) Project.copy()
+ */
+public interface CopySpec extends CopySourceSpec, CopyProcessingSpec, PatternFilterable {
+    /**
+     * Specifies whether case-sensitive pattern matching should be used.
+     *
+     * @return true for case-sensitive matching.
+     */
+    boolean isCaseSensitive();
+
+    /**
+     * Specifies whether case-sensitive pattern matching should be used for this CopySpec.
+     *
+     * @param caseSensitive true for case-sensitive matching.
+     */
+    void setCaseSensitive(boolean caseSensitive);
+
+    /**
+     * Tells if empty target directories will be included in the copy.
+     *
+     * @return <tt>true</tt> if empty target directories will be included in the copy, <tt>false</tt> otherwise
+     */
+    boolean getIncludeEmptyDirs();
+
+    /**
+     * Controls if empty target directories should be included in the copy.
+     *
+     * @param includeEmptyDirs <tt>true</tt> if empty target directories should be included in the copy, <tt>false</tt> otherwise
+     */
+    void setIncludeEmptyDirs(boolean includeEmptyDirs);
+
+    /**
+     * Adds the given specs as a child of this spec.
+     * @param sourceSpecs The specs to add
+     * @return this
+     */
+    CopySpec with(CopySpec... sourceSpecs);
+
+    // CopySourceSpec overrides to broaden return type
+
+    /**
+     * {@inheritDoc}
+     */
+    CopySpec from(Object... sourcePaths);
+
+    /**
+     * {@inheritDoc}
+     */
+    CopySpec from(Object sourcePath, Closure c);
+
+    // PatternFilterable overrides to broaden return type
+
+    /**
+     * {@inheritDoc}
+     *
+     * @see org.gradle.api.tasks.util.PatternFilterable Pattern Format
+     */
+    CopySpec setIncludes(Iterable<String> includes);
+
+    /**
+     * {@inheritDoc}
+     *
+     * @see org.gradle.api.tasks.util.PatternFilterable Pattern Format
+     */
+    CopySpec setExcludes(Iterable<String> excludes);
+
+    /**
+     * {@inheritDoc}
+     *
+     * @see org.gradle.api.tasks.util.PatternFilterable Pattern Format
+     */
+    CopySpec include(String... includes);
+
+    /**
+     * {@inheritDoc}
+     *
+     * @see org.gradle.api.tasks.util.PatternFilterable Pattern Format
+     */
+    CopySpec include(Iterable<String> includes);
+
+    /**
+     * {@inheritDoc}
+     *
+     * @see org.gradle.api.tasks.util.PatternFilterable Pattern Format
+     */
+    CopySpec include(Spec<FileTreeElement> includeSpec);
+
+    /**
+     * {@inheritDoc}
+     *
+     * @see org.gradle.api.tasks.util.PatternFilterable Pattern Format
+     */
+    CopySpec include(Closure includeSpec);
+
+    /**
+     * {@inheritDoc}
+     *
+     * @see org.gradle.api.tasks.util.PatternFilterable Pattern Format
+     */
+    CopySpec exclude(String... excludes);
+
+    /**
+     * {@inheritDoc}
+     *
+     * @see org.gradle.api.tasks.util.PatternFilterable Pattern Format
+     */
+    CopySpec exclude(Iterable<String> excludes);
+
+    /**
+     * {@inheritDoc}
+     *
+     * @see org.gradle.api.tasks.util.PatternFilterable Pattern Format
+     */
+    CopySpec exclude(Spec<FileTreeElement> excludeSpec);
+
+    /**
+     * {@inheritDoc}
+     *
+     * @see org.gradle.api.tasks.util.PatternFilterable Pattern Format
+     */
+    CopySpec exclude(Closure excludeSpec);
+
+    // CopyProcessingSpec overrides to broaden return type
+
+    /**
+     * {@inheritDoc}
+     */
+    CopySpec into(Object destPath);
+
+    /**
+     * Creates and configures a child {@code CopySpec} with the given destination path. The destination path is evaluated based on its type, as follows:
+     *
+     * <ul>
+     *
+     * <li>A closure: The return value of the closure is recursively evaluated.</li>
+     *
+     * <li>Anything else: The {@code toString()} value of the given path.</li>
+     *
+     * </ul>
+     *
+     * @param destPath Path to the destination directory for a Copy
+     * @param configureClosure The closure to use to configure the child {@code CopySpec}.
+     * @return this
+     */
+    CopySpec into(Object destPath, Closure configureClosure);
+
+    /**
+     * {@inheritDoc}
+     */
+    CopySpec rename(Closure closure);
+
+    /**
+     * {@inheritDoc}
+     */
+    CopySpec rename(String sourceRegEx, String replaceWith);
+
+    /**
+     * {@inheritDoc}
+     */
+    CopyProcessingSpec rename(Pattern sourceRegEx, String replaceWith);
+
+    /**
+     * {@inheritDoc}
+     */
+    CopySpec filter(Map<String, ?> properties, Class<? extends FilterReader> filterType);
+
+    /**
+     * {@inheritDoc}
+     */
+    CopySpec filter(Class<? extends FilterReader> filterType);
+
+    /**
+     * {@inheritDoc}
+     */
+    CopySpec filter(Closure closure);
+
+    /**
+     * {@inheritDoc}
+     */
+    CopySpec expand(Map<String, ?> properties);
+
+    /**
+     * {@inheritDoc}
+     */
+    CopySpec eachFile(Action<? super FileCopyDetails> action);
+
+    /**
+     * {@inheritDoc}
+     */
+    CopySpec eachFile(Closure closure);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/file/DirectoryTree.java b/subprojects/core/src/main/groovy/org/gradle/api/file/DirectoryTree.java
new file mode 100644
index 0000000..7248f94
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/file/DirectoryTree.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.util.PatternSet;
+
+import java.io.File;
+
+/**
+ * <p>A directory with some associated include and exclude patterns.</p>
+ *
+ * <p>This interface does not allow mutation. However, the actual implementation may not be immutable.</p>
+ */
+public interface DirectoryTree {
+    /**
+     * Returns the base directory of this tree.
+     * @return The base dir, never returns null.
+     */
+    File getDir();
+
+    /**
+     * Returns the patterns which select the files under the base directory.
+     *
+     * @return The patterns, never returns null.
+     */
+    PatternSet getPatterns();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/file/SourceDirectorySet.java b/subprojects/core/src/main/groovy/org/gradle/api/file/SourceDirectorySet.java
index 55cb9dc..c53755c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/file/SourceDirectorySet.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/file/SourceDirectorySet.java
@@ -46,7 +46,7 @@ public interface SourceDirectorySet extends FileTree, PatternFilterable {
     SourceDirectorySet srcDirs(Object... srcPaths);
 
     /**
-     * Returns the source directories which make up this set.
+     * Returns the source directories which make up this set. Does not filter source directories which do not exist.
      *
      * @return The source directories. Returns an empty set when this set contains no source directories.
      */
@@ -61,6 +61,21 @@ public interface SourceDirectorySet extends FileTree, PatternFilterable {
     SourceDirectorySet setSrcDirs(Iterable<Object> srcPaths);
 
     /**
+     * Adds the given source to this set.
+     *
+     * @param source The source to add.
+     * @return this
+     */
+    SourceDirectorySet source(SourceDirectorySet source);
+    
+    /**
+     * Returns the source directory trees which make up this set. Does not filter source directories which do not exist.
+     *
+     * @return The source directory trees. Returns an empty set when this set contains no source directories.
+     */
+    Set<DirectoryTree> getSrcDirTrees();
+
+    /**
      * Returns the filter used to select the source from the source directories. These filter patterns are applied after
      * the include and exclude patterns of the source directory set itself. Generally, the filter patterns are used to
      * select certain types of files, eg {@code *.java}.
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractClassPathProvider.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractClassPathProvider.java
index d0b8461..3873844 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractClassPathProvider.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractClassPathProvider.java
@@ -30,8 +30,9 @@ import java.util.regex.Pattern;
 public abstract class AbstractClassPathProvider implements ClassPathProvider, GradleDistributionLocator {
     private final List<Pattern> all = Arrays.asList(Pattern.compile(".+"));
     private final Map<String, List<Pattern>> classPaths = new HashMap<String, List<Pattern>>();
-    private final Scanner pluginLibs;
     private final Scanner runtimeLibs;
+    private final Scanner pluginLibs;
+    private final Scanner coreImplLibs;
     private final File gradleHome;
 
     protected AbstractClassPathProvider() {
@@ -41,11 +42,13 @@ public abstract class AbstractClassPathProvider implements ClassPathProvider, Gr
             gradleHome = codeSource.getParentFile().getParentFile();
             runtimeLibs = new DirScanner(new File(gradleHome + "/lib"));
             pluginLibs = new DirScanner(new File(gradleHome + "/lib/plugins"));
+            coreImplLibs = new DirScanner(new File(gradleHome + "/lib/core-impl"));
         } else {
             // Loaded from a classes dir - assume we're running from the ide or tests
             gradleHome = null;
             runtimeLibs = new ClassPathScanner(codeSource);
             pluginLibs = runtimeLibs;
+            coreImplLibs = runtimeLibs;
         }
     }
 
@@ -67,18 +70,23 @@ public abstract class AbstractClassPathProvider implements ClassPathProvider, Gr
 
     public Set<File> findClassPath(String name) {
         Set<File> matches = new LinkedHashSet<File>();
+        if (name.equals("GRADLE_RUNTIME")) {
+            runtimeLibs.find(all, matches);
+            return matches;
+        }
         if (name.equals("GRADLE_PLUGINS")) {
             pluginLibs.find(all, matches);
             return matches;
         }
-        if (name.equals("GRADLE_RUNTIME")) {
-            runtimeLibs.find(all, matches);
+        if (name.equals("GRADLE_CORE_IMPL")) {
+            coreImplLibs.find(all, matches);
             return matches;
         }
         List<Pattern> classPathPatterns = classPaths.get(name);
         if (classPathPatterns != null) {
             runtimeLibs.find(classPathPatterns, matches);
             pluginLibs.find(classPathPatterns, matches);
+            coreImplLibs.find(classPathPatterns, matches);
             return matches;
         }
         return null;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractMultiCauseException.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractMultiCauseException.java
new file mode 100644
index 0000000..f742ebd
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractMultiCauseException.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.api.GradleException;
+import org.gradle.util.GUtil;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.util.List;
+
+public class AbstractMultiCauseException extends GradleException implements MultiCauseException {
+    private List<Throwable> causes;
+    private final ThreadLocal<Boolean> hideCause = new ThreadLocal<Boolean>() {
+        @Override
+        protected Boolean initialValue() {
+            return false;
+        }
+    };
+
+    public AbstractMultiCauseException(String message, Iterable<? extends Throwable> causes) {
+        super(message);
+        this.causes = GUtil.addLists(causes);
+    }
+
+    public List<? extends Throwable> getCauses() {
+        return causes;
+    }
+
+    @Override
+    public Throwable getCause() {
+        if (hideCause.get()) {
+            return null;
+        }
+        return causes.isEmpty() ? null : causes.get(0);
+    }
+
+    @Override
+    public void printStackTrace(PrintStream printStream) {
+        PrintWriter writer = new PrintWriter(printStream);
+        printStackTrace(writer);
+        writer.flush();
+    }
+
+    @Override
+    public void printStackTrace(PrintWriter printWriter) {
+        if (causes.size() <= 1) {
+            super.printStackTrace(printWriter);
+            return;
+        }
+
+        hideCause.set(true);
+        try {
+            super.printStackTrace(printWriter);
+            for (int i = 0; i < causes.size(); i++) {
+                Throwable cause = causes.get(i);
+                printWriter.format("Cause %s: ", i + 1);
+                cause.printStackTrace(printWriter);
+            }
+        } finally {
+            hideCause.set(false);
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractTask.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractTask.java
index 8c39ba0..31278dd 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractTask.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractTask.java
@@ -28,7 +28,11 @@ import org.gradle.api.internal.tasks.DefaultTaskDependency;
 import org.gradle.api.internal.tasks.TaskDependencyInternal;
 import org.gradle.api.internal.tasks.TaskExecuter;
 import org.gradle.api.internal.tasks.TaskStateInternal;
-import org.gradle.api.logging.*;
+import org.gradle.api.internal.tasks.execution.TaskValidator;
+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.specs.AndSpec;
 import org.gradle.api.specs.Spec;
@@ -85,6 +89,8 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
 
     private final LoggingManagerInternal loggingManager;
 
+    private List<TaskValidator> validators = new ArrayList<TaskValidator>();
+
     protected AbstractTask() {
         this(taskInfo());
     }
@@ -261,21 +267,6 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
         return this;
     }
 
-    public boolean equals(Object other) {
-        if (other == this) {
-            return true;
-        }
-        if (other == null || other.getClass() != getClass()) {
-            return false;
-        }
-        AbstractTask otherTask = (AbstractTask) other;
-        return getPath().equals(otherTask.getPath());
-    }
-
-    public int hashCode() {
-        return path.hashCode();
-    }
-
     public int compareTo(Task otherTask) {
         int depthCompare = project.compareTo(otherTask.getProject());
         if (depthCompare == 0) {
@@ -413,6 +404,14 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
         return dir;
     }
 
+    public void addValidator(TaskValidator validator) {
+        validators.add(validator);
+    }
+
+    public List<TaskValidator> getValidators() {
+        return validators;
+    }
+
     private Action<Task> convertClosureToAction(Closure actionClosure) {
         actionClosure.setDelegate(this);
         actionClosure.setResolveStrategy(Closure.DELEGATE_FIRST);
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/Contextual.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/Contextual.java
index fd29917..36b140e 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/Contextual.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/Contextual.java
@@ -21,9 +21,6 @@ import java.lang.annotation.*;
  * This annotation is attached to an exception class to indicate that it provides contextual information about the
  * exception which might help the user determine what the failed operation was, or where it took place. Generally, this
  * annotation is only attached to exceptions which chain lower-level exceptions.
- *
- * A contextual exception class should declare a no-args constructor or a copy constructor, to allow automated
- * generation of subclasses by an {@link org.gradle.api.internal.ExceptionAnalyser}.
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultAutoCreateDomainObjectContainer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultAutoCreateDomainObjectContainer.java
index 732fb4d..301d2e4 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultAutoCreateDomainObjectContainer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultAutoCreateDomainObjectContainer.java
@@ -20,9 +20,9 @@ import org.gradle.api.NamedDomainObjectFactory;
 import org.gradle.util.ReflectionUtil;
 
 public class DefaultAutoCreateDomainObjectContainer<T> extends AutoCreateDomainObjectContainer<T> {
-    private final NamedDomainObjectFactory<? extends T> factory;
+    private final NamedDomainObjectFactory<T> factory;
 
-    public DefaultAutoCreateDomainObjectContainer(Class<T> type, ClassGenerator classGenerator, NamedDomainObjectFactory<? extends T> factory) {
+    public DefaultAutoCreateDomainObjectContainer(Class<T> type, ClassGenerator classGenerator, NamedDomainObjectFactory<T> factory) {
         super(type, classGenerator);
         this.factory = factory;
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultClassPathProvider.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultClassPathProvider.java
index 5ddf321..565480a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultClassPathProvider.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultClassPathProvider.java
@@ -1,36 +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.api.internal;
-
-import java.util.List;
-import java.util.regex.Pattern;
-
-public class DefaultClassPathProvider extends AbstractClassPathProvider {
-    public DefaultClassPathProvider() {
-        List<Pattern> groovyPatterns = toPatterns("groovy-all");
-
-        add("LOCAL_GROOVY", groovyPatterns);
-        List<Pattern> gradleApiPatterns = toPatterns("gradle-\\w+", "ivy", "slf4j", "ant");
-        gradleApiPatterns.addAll(groovyPatterns);
-        // Add the test fixture runtime, too
-        gradleApiPatterns.addAll(toPatterns("commons-io", "asm", "commons-lang", "commons-collections", "maven-ant-tasks"));
-        add("GRADLE_API", gradleApiPatterns);
-        add("GRADLE_CORE", toPatterns("gradle-core"));
-        add("ANT", toPatterns("ant", "ant-launcher"));
-        add("COMMONS_CLI", toPatterns("commons-cli"));
-    }
-}
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 java.util.List;
+import java.util.regex.Pattern;
+
+public class DefaultClassPathProvider extends AbstractClassPathProvider {
+    public DefaultClassPathProvider() {
+        List<Pattern> groovyPatterns = toPatterns("groovy-all");
+
+        add("LOCAL_GROOVY", groovyPatterns);
+        List<Pattern> gradleApiPatterns = toPatterns("gradle-\\w+", "ivy", "slf4j", "ant");
+        gradleApiPatterns.addAll(groovyPatterns);
+        // Add the test fixture runtime, too
+        gradleApiPatterns.addAll(toPatterns("commons-io", "asm", "commons-lang", "commons-collections", "maven-ant-tasks"));
+        add("GRADLE_API", gradleApiPatterns);
+        add("GRADLE_CORE", toPatterns("gradle-core"));
+        add("ANT", toPatterns("ant", "ant-launcher"));
+        add("COMMONS_CLI", toPatterns("commons-cli"));
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/MultiCauseException.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/MultiCauseException.java
new file mode 100644
index 0000000..95822b2
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/MultiCauseException.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 java.util.List;
+
+public interface MultiCauseException {
+    List<? extends Throwable> getCauses();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/TaskInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/TaskInternal.java
index 53e2514..6763825 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/TaskInternal.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/TaskInternal.java
@@ -18,10 +18,13 @@ package org.gradle.api.internal;
 
 import org.gradle.api.Task;
 import org.gradle.api.internal.tasks.TaskExecuter;
-import org.gradle.logging.StandardOutputCapture;
+import org.gradle.api.internal.tasks.execution.TaskValidator;
 import org.gradle.api.specs.Spec;
+import org.gradle.logging.StandardOutputCapture;
 import org.gradle.util.Configurable;
 
+import java.util.List;
+
 public interface TaskInternal extends Task, Configurable<Task> {
     Spec<? super TaskInternal> getOnlyIf();
 
@@ -37,4 +40,8 @@ public interface TaskInternal extends Task, Configurable<Task> {
     void setExecuter(TaskExecuter executer);
 
     TaskOutputsInternal getOutputs();
+
+    List<TaskValidator> getValidators();
+
+    void addValidator(TaskValidator validator);
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/XmlTransformer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/XmlTransformer.java
index 6e04018..52f8aa2 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/XmlTransformer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/XmlTransformer.java
@@ -25,6 +25,7 @@ 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.SystemProperties;
 import org.gradle.util.TextUtil;
 import org.gradle.util.UncheckedException;
 import org.w3c.dom.Document;
@@ -186,9 +187,9 @@ public class XmlTransformer implements Transformer<String> {
                 } else if (element != null) {
                     printNode(element, writer);
                 } else if (builder != null) {
-                    writer.append(TextUtil.toNativeLineSeparators(removeAnyXmlDeclaration(builder)));
+                    writer.append(TextUtil.toPlatformLineSeparators(stripXmlDeclaration(builder)));
                 } else {
-                    writer.append(TextUtil.toNativeLineSeparators(removeAnyXmlDeclaration(stringValue)));
+                    writer.append(TextUtil.toPlatformLineSeparators(stripXmlDeclaration(stringValue)));
                 }
             } catch (IOException e) {
                 throw UncheckedException.asUncheckedException(e);
@@ -253,7 +254,7 @@ public class XmlTransformer implements Transformer<String> {
                     writer.write("\"");
                 }
                 writer.write("?>");
-                writer.write(TextUtil.LINE_SEPARATOR);
+                writer.write(SystemProperties.getLineSeparator());
             } catch (IOException e) {
                 throw UncheckedException.asUncheckedException(e);
             }
@@ -262,7 +263,7 @@ public class XmlTransformer implements Transformer<String> {
             return xml.startsWith("<?xml"); // XML declarations must be located at first position of first line
         }
 
-        private String removeAnyXmlDeclaration(CharSequence sequence) {
+        private String stripXmlDeclaration(CharSequence sequence) {
             String str = sequence.toString();
             if (hasXmlDeclaration(str)) {
                 str = str.substring(str.indexOf("?>") + 2);
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DependencyManagementServices.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DependencyManagementServices.java
new file mode 100644
index 0000000..2c1a777
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DependencyManagementServices.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.api.internal.project.ServiceRegistry;
+
+/**
+ * Factory for various types related to dependency management.
+ *
+ * <p>The motivation for having this factory is to allow implementation
+ * types, and more importantly their dependencies, to be loaded from a
+ * different (coreImpl) class loader. This helps to prevent version conflicts,
+ * for example between Maven 2 and Maven 3 libraries.
+ */
+public interface DependencyManagementServices extends ServiceRegistry {
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/IvyService.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/IvyService.java
index 40ef9ed..729cb0e 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/IvyService.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/IvyService.java
@@ -17,6 +17,7 @@ package org.gradle.api.internal.artifacts;
 
 import org.apache.ivy.plugins.resolver.DependencyResolver;
 import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.PublishException;
 import org.gradle.api.artifacts.ResolveException;
 import org.gradle.api.artifacts.ResolvedConfiguration;
 
@@ -31,5 +32,5 @@ public interface IvyService {
     ResolvedConfiguration resolve(Configuration configuration) throws ResolveException;
 
     void publish(Set<Configuration> configurationsToPublish, File descriptorDestination,
-                 List<DependencyResolver> publishResolvers);
+                 List<DependencyResolver> publishResolvers) throws PublishException;
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfiguration.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfiguration.java
index 78827cc..5f1b382 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfiguration.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfiguration.java
@@ -341,24 +341,6 @@ public class DefaultConfiguration extends AbstractFileCollection implements Conf
         return Configurations.uploadTaskName(getName());
     }
 
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        DefaultConfiguration that = (DefaultConfiguration) o;
-        return path.equals(that.path);
-    }
-
-    @Override
-    public int hashCode() {
-        return path.hashCode();
-    }
-
     public String getDisplayName() {
         return String.format("configuration '%s'", path);
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultProjectDependency.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultProjectDependency.java
index 4da0820..6924114 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultProjectDependency.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultProjectDependency.java
@@ -89,13 +89,10 @@ public class DefaultProjectDependency extends AbstractModuleDependency implement
     @Override
     public void resolve(DependencyResolveContext context) {
         boolean transitive = isTransitive() && context.isTransitive();
-        for (Dependency dependency : getProjectConfiguration().getAllDependencies()) {
-            if (!(dependency instanceof ProjectDependency)) {
-                context.add(dependency);
-            } else if (transitive) {
+        if (transitive) {
+            for (Dependency dependency : getProjectConfiguration().getAllDependencies()) {
                 context.add(dependency);
             }
-            // else project dep and non-transitive, so skip
         }
     }
 
@@ -162,4 +159,4 @@ public class DefaultProjectDependency extends AbstractModuleDependency implement
             context.add(configuration.getBuildArtifacts());
         }
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/DefaultRepositoryHandler.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/DefaultRepositoryHandler.java
index b68e8d3..c2ded27 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/DefaultRepositoryHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/DefaultRepositoryHandler.java
@@ -19,13 +19,18 @@ import groovy.lang.Closure;
 import org.apache.ivy.plugins.resolver.AbstractResolver;
 import org.apache.ivy.plugins.resolver.DependencyResolver;
 import org.apache.ivy.plugins.resolver.FileSystemResolver;
+import org.gradle.api.Action;
 import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.artifacts.dsl.ArtifactRepository;
+import org.gradle.api.artifacts.dsl.IvyArtifactRepository;
 import org.gradle.api.artifacts.dsl.RepositoryHandler;
 import org.gradle.api.artifacts.maven.GroovyMavenDeployer;
 import org.gradle.api.artifacts.maven.MavenResolver;
 import org.gradle.api.internal.ClassGenerator;
 import org.gradle.api.internal.artifacts.DefaultResolverContainer;
 import org.gradle.api.internal.artifacts.ivyservice.ResolverFactory;
+import org.gradle.api.internal.artifacts.repositories.ArtifactRepositoryInternal;
+import org.gradle.util.ConfigureUtil;
 import org.gradle.util.GUtil;
 import org.gradle.util.HashUtil;
 import org.gradle.util.WrapUtil;
@@ -172,4 +177,34 @@ public class DefaultRepositoryHandler extends DefaultResolverContainer implement
         mavenInstaller.setName(getNameFromMap(args, defaultName));
         return mavenInstaller;
     }
+
+    public IvyArtifactRepository ivy(Action<? super IvyArtifactRepository> action) {
+        IvyArtifactRepository repository = getResolverFactory().createIvyRepository();
+        addRepository(repository, action);
+        return repository;
+    }
+
+    public IvyArtifactRepository ivy(Closure closure) {
+        IvyArtifactRepository repository = getResolverFactory().createIvyRepository();
+        addRepository((ArtifactRepositoryInternal) repository, closure);
+        return repository;
+    }
+
+    private <T extends ArtifactRepository> void addRepository(T repository, Action<? super T> action) {
+        action.execute(repository);
+        addRepository((ArtifactRepositoryInternal) repository);
+    }
+
+    private void addRepository(ArtifactRepositoryInternal repository, Closure closure) {
+        ConfigureUtil.configure(closure, repository);
+        addRepository(repository);
+    }
+
+    private void addRepository(ArtifactRepositoryInternal repository) {
+        List<DependencyResolver> resolvers = new ArrayList<DependencyResolver>();
+        repository.createResolvers(resolvers);
+        for (DependencyResolver resolver : resolvers) {
+            add(resolver);
+        }
+    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/SharedConventionRepositoryHandlerFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/SharedConventionRepositoryHandlerFactory.java
index c347c2c..a19f08b 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/SharedConventionRepositoryHandlerFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/SharedConventionRepositoryHandlerFactory.java
@@ -22,11 +22,11 @@ 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 Factory<RepositoryHandler> factory;
     private final Convention convention;
     private ConventionMapping conventionMapping;
 
-    public SharedConventionRepositoryHandlerFactory(Factory<? extends RepositoryHandler> factory, Convention convention) {
+    public SharedConventionRepositoryHandlerFactory(Factory<RepositoryHandler> factory, Convention convention) {
         this.factory = factory;
         this.convention = convention;
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ClientModuleResolver.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ClientModuleResolver.java
index ea4db3a..2d78011 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ClientModuleResolver.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ClientModuleResolver.java
@@ -17,27 +17,20 @@
 package org.gradle.api.internal.artifacts.ivyservice;
 
 import org.apache.ivy.core.IvyContext;
-import org.apache.ivy.core.cache.DefaultRepositoryCacheManager;
-import org.apache.ivy.core.cache.RepositoryCacheManager;
 import org.apache.ivy.core.module.descriptor.Artifact;
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
 import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
 import org.apache.ivy.core.report.DownloadStatus;
 import org.apache.ivy.core.report.MetadataArtifactDownloadReport;
 import org.apache.ivy.core.resolve.ResolveData;
 import org.apache.ivy.core.resolve.ResolvedModuleRevision;
-import org.apache.ivy.plugins.lock.NoLockStrategy;
 import org.apache.ivy.plugins.repository.Resource;
 import org.apache.ivy.plugins.resolver.BasicResolver;
 import org.apache.ivy.plugins.resolver.DependencyResolver;
 import org.apache.ivy.plugins.resolver.util.ResolvedResource;
-import org.gradle.api.UncheckedIOException;
 import org.gradle.api.artifacts.ClientModule;
-import org.gradle.api.artifacts.ResolverContainer;
-import org.gradle.util.DeleteOnExit;
 
 import java.io.File;
-import java.io.IOException;
 import java.util.Collection;
 import java.util.Date;
 import java.util.Map;
@@ -46,14 +39,14 @@ import java.util.Map;
  * @author Hans Dockter
  */
 public class ClientModuleResolver extends BasicResolver {
-    private Map moduleRegistry;
+    private Map<String, ModuleDescriptor> moduleRegistry;
     private DependencyResolver userResolver;
 
-    public ClientModuleResolver(String name, Map moduleRegistry, DependencyResolver userResolver) {
+    public ClientModuleResolver(String name, Map<String, ModuleDescriptor> moduleRegistry, DependencyResolver userResolver) {
         setName(name);
         this.moduleRegistry = moduleRegistry;
         this.userResolver = userResolver;
-        setRepositoryCacheManager(createUseOriginCacheManager(name));
+        setRepositoryCacheManager(new NoOpRepositoryCacheManager(name));
     }
 
     public ResolvedModuleRevision getDependency(DependencyDescriptor dde, ResolveData data) {
@@ -65,8 +58,7 @@ public class ClientModuleResolver extends BasicResolver {
         try {
             context.setDependencyDescriptor(dde);
             context.setResolveData(data);
-            DefaultModuleDescriptor moduleDescriptor =
-                    (DefaultModuleDescriptor) moduleRegistry.get(dde.getExtraAttribute(ClientModule.CLIENT_MODULE_KEY));
+            ModuleDescriptor moduleDescriptor = moduleRegistry.get(dde.getExtraAttribute(ClientModule.CLIENT_MODULE_KEY));
             MetadataArtifactDownloadReport downloadReport = new MetadataArtifactDownloadReport(moduleDescriptor.getMetadataArtifact());
             downloadReport.setDownloadStatus(DownloadStatus.NO);
             downloadReport.setSearched(false);
@@ -99,28 +91,4 @@ public class ClientModuleResolver extends BasicResolver {
     public void publish(Artifact artifact, File src, boolean overwrite) {
     }
 
-    private RepositoryCacheManager createUseOriginCacheManager(String name) {
-        File tmpIvyCache = createTmpDir();
-        DefaultRepositoryCacheManager cacheManager = new DefaultRepositoryCacheManager();
-        cacheManager.setBasedir(tmpIvyCache);
-        cacheManager.setName(name);
-        cacheManager.setUseOrigin(true);
-        cacheManager.setLockStrategy(new NoLockStrategy());
-        cacheManager.setIvyPattern(ResolverContainer.DEFAULT_CACHE_IVY_PATTERN);
-        cacheManager.setArtifactPattern(ResolverContainer.DEFAULT_CACHE_ARTIFACT_PATTERN);
-        return cacheManager;
-    }
-
-    private File createTmpDir() {
-        File tmpFile;
-        try {
-            tmpFile = File.createTempFile("gradle_ivy_cache_" + getName(), "");
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
-        tmpFile.delete();
-        tmpFile.mkdir();
-        DeleteOnExit.addFile(tmpFile);
-        return tmpFile;
-    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyDependencyResolver.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyDependencyResolver.java
index f48ed9e..cf9cc40 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyDependencyResolver.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyDependencyResolver.java
@@ -102,18 +102,11 @@ public class DefaultIvyDependencyResolver implements IvyDependencyResolver {
         }
 
         public Set<File> getFiles(Spec<Dependency> dependencySpec) {
-            rethrowFailure();
-            Set<ModuleDependency> allDependencies = configuration.getAllDependencies(ModuleDependency.class);
-            Set<ModuleDependency> selectedDependencies = Specs.filterIterable(allDependencies, dependencySpec);
-
             Set<ResolvedArtifact> artifacts = new LinkedHashSet<ResolvedArtifact>();
 
-            for (ModuleDependency moduleDependency : selectedDependencies) {
-                Set<ResolvedDependency> resolvedDependencies = conversionResult.getFirstLevelResolvedDependencies().get(moduleDependency);
-                for (ResolvedDependency resolvedDependency : resolvedDependencies) {
-                    artifacts.addAll(resolvedDependency.getParentArtifacts(conversionResult.getRoot()));
-                    walker.add(resolvedDependency);
-                }
+            for (ResolvedDependency resolvedDependency : getFirstLevelModuleDependencies(dependencySpec)) {
+                artifacts.addAll(resolvedDependency.getParentArtifacts(conversionResult.getRoot()));
+                walker.add(resolvedDependency);
             }
 
             artifacts.addAll(walker.findValues());
@@ -135,6 +128,19 @@ public class DefaultIvyDependencyResolver implements IvyDependencyResolver {
             return conversionResult.getRoot().getChildren();
         }
 
+        public Set<ResolvedDependency> getFirstLevelModuleDependencies(Spec<Dependency> dependencySpec) {
+            rethrowFailure();
+            Set<ModuleDependency> allDependencies = configuration.getAllDependencies(ModuleDependency.class);
+            Set<ModuleDependency> selectedDependencies = Specs.filterIterable(allDependencies, dependencySpec);
+
+            Set<ResolvedDependency> result = new LinkedHashSet<ResolvedDependency>();
+            for (ModuleDependency moduleDependency : selectedDependencies) {
+                result.addAll(conversionResult.getFirstLevelResolvedDependencies().get(moduleDependency));
+            }
+
+            return result;
+        }
+
         public Set<ResolvedArtifact> getResolvedArtifacts() {
             rethrowFailure();
             return conversionResult.getResolvedArtifacts();
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultResolverFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultResolverFactory.java
deleted file mode 100644
index 2eb194d..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultResolverFactory.java
+++ /dev/null
@@ -1,188 +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.internal.artifacts.ivyservice;
-
-import org.apache.ivy.core.cache.DefaultRepositoryCacheManager;
-import org.apache.ivy.core.cache.RepositoryCacheManager;
-import org.apache.ivy.plugins.lock.NoLockStrategy;
-import org.apache.ivy.plugins.resolver.*;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.UncheckedIOException;
-import org.gradle.api.artifacts.ConfigurationContainer;
-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.file.FileResolver;
-import org.gradle.logging.LoggingManagerInternal;
-import org.gradle.util.DeleteOnExit;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Map;
-
-/**
- * @author Hans Dockter
- */
-public class DefaultResolverFactory implements ResolverFactory {
-    private final Factory<? extends LoggingManagerInternal> loggingManagerFactory;
-    private final LocalMavenCacheLocator localMavenCacheLocator;
-
-    public DefaultResolverFactory(Factory<? extends LoggingManagerInternal> loggingManagerFactory) {
-        this(loggingManagerFactory, new LocalMavenCacheLocator());
-    }
-
-    DefaultResolverFactory(Factory<? extends LoggingManagerInternal> loggingManagerFactory, LocalMavenCacheLocator localMavenCacheLocator) {
-        this.loggingManagerFactory = loggingManagerFactory;
-        this.localMavenCacheLocator = localMavenCacheLocator;
-    }
-
-    public DependencyResolver createResolver(Object userDescription) {
-        DependencyResolver result;
-        if (userDescription instanceof String) {
-            result = createMavenRepoResolver((String) userDescription, (String) userDescription);
-        } else if (userDescription instanceof Map) {
-            Map<String, String> userDescriptionMap = (Map<String, String>) userDescription;
-            result = createMavenRepoResolver(userDescriptionMap.get(ResolverContainer.RESOLVER_NAME),
-                    userDescriptionMap.get(ResolverContainer.RESOLVER_URL));
-        } else if (userDescription instanceof DependencyResolver) {
-            result = (DependencyResolver) userDescription;
-        } else {
-            throw new InvalidUserDataException("Illegal Resolver type");
-        }
-        return result;
-    }
-
-    public FileSystemResolver createFlatDirResolver(String name, File... roots) {
-        FileSystemResolver resolver = new FileSystemResolver();
-        resolver.setName(name);
-        for (File root : roots) {
-            String pattern = root.getAbsolutePath() + "/" + ResolverContainer.FLAT_DIR_RESOLVER_PATTERN;
-            resolver.addArtifactPattern(pattern);
-        }
-        resolver.setValidate(false);
-        resolver.setRepositoryCacheManager(createUseOriginCacheManager(name));
-        return resolver;
-    }
-
-    private RepositoryCacheManager createUseOriginCacheManager(String name) {
-        File tmpIvyCache = createTmpDir();
-        DefaultRepositoryCacheManager cacheManager = new DefaultRepositoryCacheManager();
-        cacheManager.setBasedir(tmpIvyCache);
-        cacheManager.setName(name);
-        cacheManager.setUseOrigin(true);
-        cacheManager.setLockStrategy(new NoLockStrategy());
-        cacheManager.setIvyPattern(ResolverContainer.DEFAULT_CACHE_IVY_PATTERN);
-        cacheManager.setArtifactPattern(ResolverContainer.DEFAULT_CACHE_ARTIFACT_PATTERN);
-        return cacheManager;
-    }
-
-    private File createTmpDir() {
-        File tmpFile;
-        try {
-            tmpFile = File.createTempFile("gradle_ivy_cache", "");
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
-        tmpFile.delete();
-        tmpFile.mkdir();
-        DeleteOnExit.addFile(tmpFile);
-        return tmpFile;
-    }
-
-    public AbstractResolver createMavenLocalResolver(String name) {
-        String cacheDir = localMavenCacheLocator.getLocalMavenCache().toURI().toString();
-        return createMavenRepoResolver(name, cacheDir);
-    }
-
-    public AbstractResolver createMavenRepoResolver(String name, String root, String... jarRepoUrls) {
-        GradleIBiblioResolver iBiblioResolver = createIBiblioResolver(name, root);
-        if (jarRepoUrls.length == 0) {
-            iBiblioResolver.setDescriptor(IBiblioResolver.DESCRIPTOR_OPTIONAL);
-            return iBiblioResolver;
-        }
-        iBiblioResolver.setName(iBiblioResolver.getName() + "_poms");
-        URLResolver urlResolver = createUrlResolver(name, root, jarRepoUrls);
-        return createDualResolver(name, iBiblioResolver, urlResolver);
-    }
-
-    private GradleIBiblioResolver createIBiblioResolver(String name, String root) {
-        GradleIBiblioResolver iBiblioResolver = new GradleIBiblioResolver();
-        iBiblioResolver.setUsepoms(true);
-        iBiblioResolver.setName(name);
-        iBiblioResolver.setRoot(root);
-        iBiblioResolver.setPattern(ResolverContainer.MAVEN_REPO_PATTERN);
-        iBiblioResolver.setM2compatible(true);
-        iBiblioResolver.setUseMavenMetadata(true);
-        return iBiblioResolver;
-    }
-
-    private URLResolver createUrlResolver(String name, String root, String... jarRepoUrls) {
-        URLResolver urlResolver = new URLResolver();
-        urlResolver.setName(name + "_jars");
-        urlResolver.setM2compatible(true);
-        urlResolver.addArtifactPattern(root + '/' + ResolverContainer.MAVEN_REPO_PATTERN);
-        for (String jarRepoUrl : jarRepoUrls) {
-            urlResolver.addArtifactPattern(jarRepoUrl + '/' + ResolverContainer.MAVEN_REPO_PATTERN);
-        }
-        return urlResolver;
-    }
-
-    private DualResolver createDualResolver(String name, GradleIBiblioResolver iBiblioResolver, URLResolver urlResolver) {
-        DualResolver dualResolver = new DualResolver();
-        dualResolver.setName(name);
-        dualResolver.setIvyResolver(iBiblioResolver);
-        dualResolver.setArtifactResolver(urlResolver);
-        dualResolver.setDescriptor(DualResolver.DESCRIPTOR_OPTIONAL);
-        return dualResolver;
-    }
-
-    // todo use MavenPluginConvention pom factory after modularization is done
-
-    public GroovyMavenDeployer createMavenDeployer(String name, MavenPomMetaInfoProvider pomMetaInfoProvider,
-                                                   ConfigurationContainer configurationContainer,
-                                                   Conf2ScopeMappingContainer scopeMapping, FileResolver fileResolver) {
-        PomFilterContainer pomFilterContainer = new BasePomFilterContainer(
-                new DefaultMavenPomFactory(configurationContainer, scopeMapping, new DefaultPomDependenciesConverter(
-                        new DefaultExcludeRuleConverter()), fileResolver));
-        return new DefaultGroovyMavenDeployer(name, pomFilterContainer, new DefaultArtifactPomContainer(
-                pomMetaInfoProvider, pomFilterContainer, new DefaultArtifactPomFactory()), loggingManagerFactory.create());
-    }
-
-    // todo use MavenPluginConvention pom factory after modularization is done
-
-    public MavenResolver createMavenInstaller(String name, MavenPomMetaInfoProvider pomMetaInfoProvider,
-                                              ConfigurationContainer configurationContainer,
-                                              Conf2ScopeMappingContainer scopeMapping, FileResolver fileResolver) {
-        PomFilterContainer pomFilterContainer = new BasePomFilterContainer(
-                new DefaultMavenPomFactory(configurationContainer, scopeMapping, new DefaultPomDependenciesConverter(
-                        new DefaultExcludeRuleConverter()), fileResolver));
-        return new BaseMavenInstaller(name, pomFilterContainer, new DefaultArtifactPomContainer(pomMetaInfoProvider,
-                pomFilterContainer, new DefaultArtifactPomFactory()), loggingManagerFactory.create());
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultSettingsConverter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultSettingsConverter.java
index 4af0700..e0f28b4 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultSettingsConverter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultSettingsConverter.java
@@ -17,8 +17,8 @@
 package org.gradle.api.internal.artifacts.ivyservice;
 
 import org.apache.commons.lang.StringUtils;
-import org.apache.ivy.core.cache.DefaultRepositoryCacheManager;
 import org.apache.ivy.core.cache.RepositoryCacheManager;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
 import org.apache.ivy.core.settings.IvySettings;
 import org.apache.ivy.plugins.matcher.PatternMatcher;
 import org.apache.ivy.plugins.repository.Repository;
@@ -36,7 +36,10 @@ import org.gradle.util.Clock;
 import org.gradle.util.WrapUtil;
 
 import java.io.File;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
 
 /**
  * @author Hans Dockter
@@ -75,19 +78,15 @@ public class DefaultSettingsConverter implements SettingsConverter {
             return ivySettings;
         }
         Clock clock = new Clock();
-        ChainResolver userResolverChain = createUserResolverChain(Collections.<DependencyResolver>emptyList(), internalRepository);
-        ClientModuleResolver clientModuleResolver = createClientModuleResolver(new HashMap(), userResolverChain);
-        ChainResolver outerChain = createOuterChain(userResolverChain, clientModuleResolver);
 
         IvySettings ivySettings = createIvySettings(gradleUserHome);
-        initializeResolvers(ivySettings, getAllResolvers(Collections.<DependencyResolver>emptyList(), publishResolvers, internalRepository, userResolverChain, clientModuleResolver, outerChain));
-        ivySettings.setDefaultResolver(CLIENT_MODULE_CHAIN_NAME);
+        initializeResolvers(ivySettings, getAllResolvers(Collections.<DependencyResolver>emptyList(), publishResolvers));
         logger.debug("Timing: Ivy convert for publish took {}", clock.getTime());
         return ivySettings;
     }
 
     public IvySettings convertForResolve(List<DependencyResolver> dependencyResolvers,
-                               File gradleUserHome, DependencyResolver internalRepository, Map clientModuleRegistry) {
+                               File gradleUserHome, DependencyResolver internalRepository, Map<String, ModuleDescriptor> clientModuleRegistry) {
         if (ivySettings != null) {
             return ivySettings;
         }
@@ -104,12 +103,10 @@ public class DefaultSettingsConverter implements SettingsConverter {
     }
 
     private List<DependencyResolver> getAllResolvers(List<DependencyResolver> classpathResolvers,
-                                                     List<DependencyResolver> otherResolvers, DependencyResolver internalRepository, 
-                                                     ChainResolver userResolverChain, ClientModuleResolver clientModuleResolver,
-                                                     ChainResolver outerChain) {
+                                                     List<DependencyResolver> otherResolvers, DependencyResolver... resolvers) {
         List<DependencyResolver> allResolvers = new ArrayList<DependencyResolver>(otherResolvers);
         allResolvers.addAll(classpathResolvers);
-        allResolvers.addAll(WrapUtil.toList(internalRepository, outerChain, clientModuleResolver, userResolverChain));
+        allResolvers.addAll(WrapUtil.toList(resolvers));
         return allResolvers;
     }
 
@@ -122,7 +119,7 @@ public class DefaultSettingsConverter implements SettingsConverter {
         return clientModuleChain;
     }
 
-    private ClientModuleResolver createClientModuleResolver(Map clientModuleRegistry, ChainResolver userResolverChain) {
+    private ClientModuleResolver createClientModuleResolver(Map<String, ModuleDescriptor> clientModuleRegistry, ChainResolver userResolverChain) {
         return new ClientModuleResolver(CLIENT_MODULE_NAME, clientModuleRegistry, userResolverChain);
     }
 
@@ -161,7 +158,13 @@ public class DefaultSettingsConverter implements SettingsConverter {
     private void initializeResolvers(IvySettings ivySettings, List<DependencyResolver> allResolvers) {
         for (DependencyResolver dependencyResolver : allResolvers) {
             ivySettings.addResolver(dependencyResolver);
-            ((DefaultRepositoryCacheManager) dependencyResolver.getRepositoryCacheManager()).setSettings(ivySettings);
+            RepositoryCacheManager cacheManager = dependencyResolver.getRepositoryCacheManager();
+            // Validate that each resolver is sharing the same cache instance (ignoring caches which don't actually cache anything)
+            if (cacheManager != ivySettings.getDefaultRepositoryCacheManager()
+                    && !(cacheManager instanceof NoOpRepositoryCacheManager)
+                    && !(cacheManager instanceof LocalFileRepositoryCacheManager)) {
+                throw new IllegalStateException(String.format("Unexpected cache manager %s for repository %s (%s)", cacheManager, dependencyResolver.getName(), dependencyResolver));
+            }
             if (dependencyResolver instanceof RepositoryResolver) {
                 Repository repository = ((RepositoryResolver) dependencyResolver).getRepository();
                 if (!repository.hasTransferListener(transferListener)) {
@@ -189,15 +192,17 @@ public class DefaultSettingsConverter implements SettingsConverter {
             }
             if (evt.getEventType() == TransferEvent.TRANSFER_STARTED) {
                 total = 0;
-                DefaultSettingsConverter.logger.lifecycle(String.format("%s %s", StringUtils.capitalize(getRequestType(evt)), evt.getResource().getName()));
-                logger = progressLoggerFactory.start(DefaultSettingsConverter.class.getName());
+                logger = progressLoggerFactory.newOperation(DefaultSettingsConverter.class);
+                String description = String.format("%s %s", StringUtils.capitalize(getRequestType(evt)), evt.getResource().getName());
+                logger.setDescription(description);
+                logger.setLoggingHeader(description);
+                logger.started();
             }
             if (evt.getEventType() == TransferEvent.TRANSFER_PROGRESS) {
                 total += evt.getLength();
                 logger.progress(String.format("%s/%s %sed", getLengthText(total), getLengthText(evt), getRequestType(evt)));
             }
-            if (evt.getEventType() == TransferEvent.TRANSFER_COMPLETED
-                    || evt.getEventType() == TransferEvent.TRANSFER_ERROR) {
+            if (evt.getEventType() == TransferEvent.TRANSFER_COMPLETED) {
                 logger.completed();
             }
         }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingIvyService.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingIvyService.java
index e1f7852..76bbae1 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingIvyService.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingIvyService.java
@@ -15,15 +15,14 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice;
 
-import org.gradle.api.internal.artifacts.IvyService;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
 import org.gradle.api.artifacts.*;
-import org.gradle.api.GradleException;
+import org.gradle.api.internal.artifacts.IvyService;
 import org.gradle.api.specs.Spec;
-import org.apache.ivy.plugins.resolver.DependencyResolver;
 
-import java.util.Set;
-import java.util.List;
 import java.io.File;
+import java.util.List;
+import java.util.Set;
 
 public class ErrorHandlingIvyService implements IvyService {
     private final IvyService ivyService;
@@ -41,8 +40,7 @@ public class ErrorHandlingIvyService implements IvyService {
         try {
             ivyService.publish(configurationsToPublish, descriptorDestination, publishResolvers);
         } catch (Throwable e) {
-            throw new GradleException(String.format("Could not publish configurations %s.", configurationsToPublish),
-                    e);
+            throw new PublishException(String.format("Could not publish configurations %s.", configurationsToPublish), e);
         }
     }
 
@@ -101,6 +99,14 @@ public class ErrorHandlingIvyService implements IvyService {
             }
         }
 
+        public Set<ResolvedDependency> getFirstLevelModuleDependencies(Spec<Dependency> dependencySpec) throws ResolveException {
+            try {
+                return resolvedConfiguration.getFirstLevelModuleDependencies(dependencySpec);
+            } catch (Throwable e) {
+                throw wrapException(e, configuration);
+            }
+        }
+
         public Set<ResolvedArtifact> getResolvedArtifacts() throws ResolveException {
             try {
                 return resolvedConfiguration.getResolvedArtifacts();
@@ -135,6 +141,10 @@ public class ErrorHandlingIvyService implements IvyService {
             throw wrapException(e, configuration);
         }
 
+        public Set<ResolvedDependency> getFirstLevelModuleDependencies(Spec<Dependency> dependencySpec) throws ResolveException {
+            throw wrapException(e, configuration);
+        }
+
         public Set<ResolvedArtifact> getResolvedArtifacts() throws ResolveException {
             throw wrapException(e, configuration);
         }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/GradleIBiblioResolver.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/GradleIBiblioResolver.java
index 93787bc..255e2f8 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/GradleIBiblioResolver.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/GradleIBiblioResolver.java
@@ -22,6 +22,8 @@ import org.apache.ivy.core.resolve.ResolveData;
 import org.apache.ivy.core.resolve.ResolvedModuleRevision;
 import org.apache.ivy.plugins.resolver.IBiblioResolver;
 import org.gradle.util.UncheckedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.File;
 import java.net.URI;
@@ -33,16 +35,29 @@ import java.util.Date;
  * @author Hans Dockter
  */
 public class GradleIBiblioResolver extends IBiblioResolver {
+    Logger logger = LoggerFactory.getLogger(GradleIBiblioResolver.class);
+
     public static final CacheTimeoutStrategy NEVER = new CacheTimeoutStrategy() {
         public boolean isCacheTimedOut(long lastResolvedTime) {
             return false;
         }
+
+        @Override
+        public String toString() {
+            return "NEVER Timeout Strategy";
+        }
     };
 
     public static final CacheTimeoutStrategy ALWAYS = new CacheTimeoutStrategy() {
         public boolean isCacheTimedOut(long lastResolvedTime) {
             return true;
         }
+
+        @Override
+        public String toString() {
+            return "ALWAYS Timeout Strategy";
+        }
+
     };
 
     public static final CacheTimeoutStrategy DAILY = new CacheTimeoutStrategy() {
@@ -60,6 +75,11 @@ public class GradleIBiblioResolver extends IBiblioResolver {
             }
             return true;
         }
+
+        @Override
+        public String toString() {
+            return "DAILY Timeout Strategy";
+        }
     };
 
     private CacheTimeoutStrategy snapshotTimeout = DAILY;
@@ -111,16 +131,20 @@ public class GradleIBiblioResolver extends IBiblioResolver {
         setChangingPattern(null);
         ResolvedModuleRevision moduleRevision = super.findModuleInCache(dd, data);
         if (moduleRevision == null) {
+            logger.debug("Dependency not found in cache.");
             setChangingPattern(".*-SNAPSHOT");
             return null;
         }
         PropertiesFile cacheProperties = getCacheProperties(dd, moduleRevision);
         Long lastResolvedTime = getLastResolvedTime(cacheProperties);
         updateCachePropertiesToCurrentTime(cacheProperties);
+        logger.debug("Using the SnapshotTimeOutStrategy=" + snapshotTimeout.toString());
         if (snapshotTimeout.isCacheTimedOut(lastResolvedTime)) {
+            logger.debug("Cache has timed out.");
             setChangingPattern(".*-SNAPSHOT");
             return null;
         } else {
+            logger.debug("Dependency found in cache..");
             return moduleRevision;
         }
     }
@@ -161,6 +185,8 @@ public class GradleIBiblioResolver extends IBiblioResolver {
     }
 
     public static class Interval implements CacheTimeoutStrategy {
+        Logger logger = LoggerFactory.getLogger(Interval.class);
+
         private long interval;
 
         public Interval(long interval) {
@@ -168,7 +194,16 @@ public class GradleIBiblioResolver extends IBiblioResolver {
         }
 
         public boolean isCacheTimedOut(long lastResolvedTime) {
-            return System.currentTimeMillis() - lastResolvedTime > interval;
+            logger.debug("Last resolved time: " + lastResolvedTime);
+            long currentTime = System.currentTimeMillis();
+            logger.debug("Current time: " + currentTime);
+            boolean timedOut = currentTime - lastResolvedTime > interval;
+            return timedOut;
+        }
+
+        @Override
+        public String toString() {
+            return "Interval{interval=" + interval + '}';
         }
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/LocalFileRepositoryCacheManager.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/LocalFileRepositoryCacheManager.java
new file mode 100644
index 0000000..d44e158
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/LocalFileRepositoryCacheManager.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core.cache.*;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.report.DownloadStatus;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.plugins.repository.ArtifactResourceResolver;
+import org.apache.ivy.plugins.repository.ResourceDownloader;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+
+import java.io.File;
+import java.text.ParseException;
+
+/**
+ * A cache manager for local repositories. Doesn't cache anything, and uses artifacts from their origin.
+ */
+public class LocalFileRepositoryCacheManager implements RepositoryCacheManager {
+    private final String name;
+
+    public LocalFileRepositoryCacheManager(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void saveResolvers(ModuleDescriptor descriptor, String metadataResolverName, String artifactResolverName) {
+    }
+
+    public ArtifactOrigin getSavedArtifactOrigin(Artifact artifact) {
+        return null;
+    }
+
+    public ResolvedModuleRevision findModuleInCache(DependencyDescriptor dd, ModuleRevisionId requestedRevisionId, CacheMetadataOptions options, String expectedResolver) {
+        return null;
+    }
+
+    public ArtifactDownloadReport download(Artifact artifact, ArtifactResourceResolver resourceResolver, ResourceDownloader resourceDownloader, CacheDownloadOptions options) {
+        long start = System.currentTimeMillis();
+        ArtifactDownloadReport report = new ArtifactDownloadReport(artifact);
+        ResolvedResource resolvedResource = resourceResolver.resolve(artifact);
+        if (resolvedResource == null) {
+            report.setDownloadStatus(DownloadStatus.FAILED);
+            report.setDownloadDetails(ArtifactDownloadReport.MISSING_ARTIFACT);
+            report.setDownloadTimeMillis(System.currentTimeMillis() - start);
+            return report;
+        }
+        assert resolvedResource.getResource().isLocal();
+        File file = new File(resolvedResource.getResource().getName());
+        assert file.isFile();
+
+        ArtifactOrigin origin = new ArtifactOrigin(artifact, true, file.getAbsolutePath());
+        report.setDownloadStatus(DownloadStatus.NO);
+        report.setArtifactOrigin(origin);
+        report.setSize(file.length());
+        report.setLocalFile(file);
+        return report;
+    }
+
+    public ResolvedModuleRevision cacheModuleDescriptor(DependencyResolver resolver, ResolvedResource orginalMetadataRef, DependencyDescriptor dd, Artifact requestedMetadataArtifact, ResourceDownloader downloader, CacheMetadataOptions options) throws ParseException {
+        return null;
+    }
+
+    public void originalToCachedModuleDescriptor(DependencyResolver resolver, ResolvedResource orginalMetadataRef, Artifact requestedMetadataArtifact, ResolvedModuleRevision rmr, ModuleDescriptorWriter writer) {
+    }
+
+    public void clean() {
+    }
+
+    public void saveResolvedRevision(ModuleRevisionId dynamicMrid, String revision) {
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/LocalMavenCacheLocator.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/LocalMavenCacheLocator.java
deleted file mode 100644
index 4585172..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/LocalMavenCacheLocator.java
+++ /dev/null
@@ -1,94 +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.artifacts.ivyservice;
-
-import org.apache.maven.settings.DefaultMavenSettingsBuilder;
-import org.apache.maven.settings.MavenSettingsBuilder;
-import org.apache.maven.settings.Settings;
-import org.gradle.api.internal.artifacts.publish.maven.pombuilder.PlexusLoggerAdapter;
-import org.gradle.util.UncheckedException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.lang.reflect.Field;
-
-/**
- * @author Steve Ebersole
- */
-public class LocalMavenCacheLocator {
-    private static final Logger LOGGER = LoggerFactory.getLogger(LocalMavenCacheLocator.class);
-    private static final String USER_HOME_MARKER = "${user.home}/";
-
-    public File getLocalMavenCache() {
-        File userHome = new File(System.getProperty("user.home"));
-        File m2Dir = new File(userHome, ".m2");
-
-        File userSettings = new File(m2Dir, "settings.xml");
-        if (userSettings.exists()) {
-            File overriddenMavenLocal = extractMavenLocal(userSettings, userHome);
-            if (overriddenMavenLocal != null) {
-                return overriddenMavenLocal;
-            }
-        }
-
-        return new File(m2Dir, "repository");
-    }
-
-    private File extractMavenLocal(File userSettings, File userHome) {
-        Settings settings = extractSettings(userSettings);
-        String override = settings.getLocalRepository();
-        if (override != null) {
-            override = override.trim();
-            if (override.length() > 0) {
-                // Nice, it does not even handle the interpolation for us, so we'll handle some common cases...
-                if (override.startsWith(USER_HOME_MARKER)) {
-                    override = userHome.getAbsolutePath() + '/' + override.substring(USER_HOME_MARKER.length());
-                }
-                return new File(override);
-            }
-        }
-        return null;
-    }
-
-    private Settings extractSettings(File userSettings) {
-        try {
-            MavenSettingsBuilder builder = buildSettingsBuilder(userSettings);
-            return builder.buildSettings();
-        } catch (Exception e) {
-            throw UncheckedException.asUncheckedException(e);
-        }
-    }
-
-    private MavenSettingsBuilder buildSettingsBuilder(File userSettings) throws Exception {
-        final String userSettingsPath = userSettings.getAbsolutePath();
-
-        DefaultMavenSettingsBuilder builder = new DefaultMavenSettingsBuilder();
-        builder.enableLogging(new PlexusLoggerAdapter(LOGGER));
-
-        Field userSettingsPathField = DefaultMavenSettingsBuilder.class.getDeclaredField("userSettingsPath");
-        userSettingsPathField.setAccessible(true);
-        userSettingsPathField.set(builder, userSettingsPath);
-
-        Field globalSettingsPathField = DefaultMavenSettingsBuilder.class.getDeclaredField("globalSettingsPath");
-        globalSettingsPathField.setAccessible(true);
-        globalSettingsPathField.set(builder, userSettingsPath);
-
-        builder.initialize();
-
-        return builder;
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/NoOpRepositoryCacheManager.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/NoOpRepositoryCacheManager.java
new file mode 100644
index 0000000..a70b393
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/NoOpRepositoryCacheManager.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core.cache.*;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.report.DownloadStatus;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.plugins.repository.ArtifactResourceResolver;
+import org.apache.ivy.plugins.repository.ResourceDownloader;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+
+import java.text.ParseException;
+
+/**
+ * A cache manager which does nothing. Only useful for local meta-data only repositories.
+ */
+public class NoOpRepositoryCacheManager implements RepositoryCacheManager {
+    private final String name;
+
+    public NoOpRepositoryCacheManager(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void saveResolvers(ModuleDescriptor descriptor, String metadataResolverName, String artifactResolverName) {
+    }
+
+    public ArtifactOrigin getSavedArtifactOrigin(Artifact artifact) {
+        return null;
+    }
+
+    public ResolvedModuleRevision findModuleInCache(DependencyDescriptor dd, ModuleRevisionId requestedRevisionId, CacheMetadataOptions options, String expectedResolver) {
+        return null;
+    }
+
+    public ArtifactDownloadReport download(Artifact artifact, ArtifactResourceResolver resourceResolver, ResourceDownloader resourceDownloader, CacheDownloadOptions options) {
+        ArtifactDownloadReport report = new ArtifactDownloadReport(null);
+        report.setDownloadStatus(DownloadStatus.NO);
+        return report;
+    }
+
+    public ResolvedModuleRevision cacheModuleDescriptor(DependencyResolver resolver, ResolvedResource orginalMetadataRef, DependencyDescriptor dd, Artifact requestedMetadataArtifact, ResourceDownloader downloader, CacheMetadataOptions options) throws ParseException {
+        return null;
+    }
+
+    public void originalToCachedModuleDescriptor(DependencyResolver resolver, ResolvedResource orginalMetadataRef, Artifact requestedMetadataArtifact, ResolvedModuleRevision rmr, ModuleDescriptorWriter writer) {
+    }
+
+    public void clean() {
+    }
+
+    public void saveResolvedRevision(ModuleRevisionId dynamicMrid, String revision) {
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ResolverFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ResolverFactory.java
index 8c283b6..3810a76 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ResolverFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ResolverFactory.java
@@ -19,6 +19,7 @@ import org.apache.ivy.plugins.resolver.AbstractResolver;
 import org.apache.ivy.plugins.resolver.DependencyResolver;
 import org.apache.ivy.plugins.resolver.FileSystemResolver;
 import org.gradle.api.artifacts.ConfigurationContainer;
+import org.gradle.api.artifacts.dsl.IvyArtifactRepository;
 import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
 import org.gradle.api.artifacts.maven.GroovyMavenDeployer;
 import org.gradle.api.artifacts.maven.MavenResolver;
@@ -44,4 +45,6 @@ public interface ResolverFactory {
 
     MavenResolver createMavenInstaller(String name, MavenPomMetaInfoProvider pomMetaInfoProvider, ConfigurationContainer configurationContainer,
                                        Conf2ScopeMappingContainer scopeMapping, FileResolver fileResolver);
+
+    IvyArtifactRepository createIvyRepository();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/SelfResolvingDependencyResolver.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/SelfResolvingDependencyResolver.java
index 557c4f3..8234815 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/SelfResolvingDependencyResolver.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/SelfResolvingDependencyResolver.java
@@ -66,6 +66,10 @@ public class SelfResolvingDependencyResolver implements IvyDependencyResolver {
                 return resolvedConfiguration.getFirstLevelModuleDependencies();
             }
 
+            public Set<ResolvedDependency> getFirstLevelModuleDependencies(Spec<Dependency> dependencySpec) throws ResolveException {
+                return resolvedConfiguration.getFirstLevelModuleDependencies(dependencySpec);
+            }
+
             public boolean hasError() {
                 return resolvedConfiguration.hasError();
             }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/SettingsConverter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/SettingsConverter.java
index fe39f48..ec11370 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/SettingsConverter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/SettingsConverter.java
@@ -15,6 +15,7 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice;
 
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
 import org.apache.ivy.core.settings.IvySettings;
 import org.apache.ivy.plugins.resolver.DependencyResolver;
 
@@ -33,5 +34,5 @@ public interface SettingsConverter {
     IvySettings convertForPublish(List<DependencyResolver> publishResolvers, File gradleUserHome, DependencyResolver internalRepository);
 
     IvySettings convertForResolve(List<DependencyResolver> classpathResolvers, File gradleUserHome, DependencyResolver internalRepository,
-                        Map clientModuleRegistry);
+                        Map<String, ModuleDescriptor> clientModuleRegistry);
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ShortcircuitEmptyConfigsIvyService.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ShortcircuitEmptyConfigsIvyService.java
index eb6cc8c..0c84ec1 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ShortcircuitEmptyConfigsIvyService.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ShortcircuitEmptyConfigsIvyService.java
@@ -42,6 +42,10 @@ public class ShortcircuitEmptyConfigsIvyService implements IvyService {
             return Collections.emptySet();
         }
 
+        public Set<ResolvedDependency> getFirstLevelModuleDependencies(Spec<Dependency> dependencySpec) throws ResolveException {
+            return Collections.emptySet();
+        }
+
         public Set<ResolvedArtifact> getResolvedArtifacts() {
             return Collections.emptySet();
         }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultArtifactPomFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultArtifactPomFactory.java
deleted file mode 100644
index 9e28f4f..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultArtifactPomFactory.java
+++ /dev/null
@@ -1,30 +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;
-import org.gradle.api.internal.artifacts.publish.maven.deploy.ArtifactPom;
-import org.gradle.api.internal.artifacts.publish.maven.deploy.ArtifactPomFactory;
-import org.gradle.api.internal.artifacts.publish.maven.deploy.DefaultArtifactPom;
-
-/**
- * @author Hans Dockter
- */
-public class DefaultArtifactPomFactory implements ArtifactPomFactory {
-    public ArtifactPom createArtifactPom(MavenPom pom) {
-        return new DefaultArtifactPom(pom);
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPom.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPom.java
deleted file mode 100644
index bf22068..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPom.java
+++ /dev/null
@@ -1,251 +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 groovy.lang.Closure;
-import org.apache.commons.io.IOUtils;
-import org.apache.maven.model.Dependency;
-import org.apache.maven.model.Model;
-import org.apache.maven.project.MavenProject;
-import org.codehaus.groovy.runtime.InvokerHelper;
-import org.gradle.api.Action;
-import org.gradle.api.UncheckedIOException;
-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;
-import org.gradle.listener.ActionBroadcast;
-
-import java.io.*;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * @author Hans Dockter
- */
-public class DefaultMavenPom implements MavenPom {
-    private PomDependenciesConverter pomDependenciesConverter;
-    private FileResolver fileResolver;
-    private MavenProject mavenProject = new MavenProject();
-    private Conf2ScopeMappingContainer scopeMappings;
-    private ActionBroadcast<MavenPom> whenConfiguredActions = new ActionBroadcast<MavenPom>();
-    private XmlTransformer withXmlActions = new XmlTransformer();
-    private ConfigurationContainer configurations;
-
-    public DefaultMavenPom(ConfigurationContainer configurationContainer, Conf2ScopeMappingContainer scopeMappings, PomDependenciesConverter pomDependenciesConverter,
-                           FileResolver fileResolver) {
-        this.configurations = configurationContainer;
-        this.scopeMappings = scopeMappings;
-        this.pomDependenciesConverter = pomDependenciesConverter;
-        this.fileResolver = fileResolver;
-        mavenProject.setModelVersion("4.0.0");
-    }
-
-    public Conf2ScopeMappingContainer getScopeMappings() {
-        return scopeMappings;
-    }
-
-    public ConfigurationContainer getConfigurations() {
-        return configurations;
-    }
-
-    public DefaultMavenPom setConfigurations(ConfigurationContainer configurations) {
-        this.configurations = configurations;
-        return this;
-    }
-    
-    public DefaultMavenPom setGroupId(String groupId) {
-        getModel().setGroupId(groupId);
-        return this;
-    }
-
-    public String getGroupId() {
-        return getModel().getGroupId();
-    }
-
-    public DefaultMavenPom setArtifactId(String artifactId) {
-        getModel().setArtifactId(artifactId);
-        return this;
-    }
-
-    public String getArtifactId() {
-        return getModel().getArtifactId();
-    }
-
-    public DefaultMavenPom setDependencies(List<Dependency> dependencies) {
-        getModel().setDependencies(dependencies);
-        return this;
-    }
-
-    public List<Dependency> getDependencies() {
-        return getModel().getDependencies();
-    }
-
-    public DefaultMavenPom setName(String name) {
-        getModel().setName(name);
-        return this;
-    }
-
-    public String getName() {
-        return getModel().getName();
-    }
-
-    public DefaultMavenPom setVersion(String version) {
-        getModel().setVersion(version);
-        return this;
-    }
-
-    public String getVersion() {
-        return getModel().getVersion();
-    }
-
-    public String getPackaging() {
-        return getModel().getPackaging();
-    }
-
-    public DefaultMavenPom setPackaging(String packaging) {
-        getModel().setPackaging(packaging);
-        return this;
-    }
-
-    public DefaultMavenPom project(Closure cl) {
-        CustomModelBuilder pomBuilder = new CustomModelBuilder(getModel());
-        InvokerHelper.invokeMethod(pomBuilder, "project", cl);
-        return this;
-    }
-
-    public Model getModel() {
-        return mavenProject.getModel();
-    }
-
-    public DefaultMavenPom setModel(Model model) {
-        this.mavenProject = new MavenProject(model);
-        return this;
-    }
-
-    public MavenProject getMavenProject() {
-        return mavenProject;
-    }
-
-    public DefaultMavenPom setMavenProject(MavenProject mavenProject) {
-        this.mavenProject = mavenProject;
-        return this;
-    }
-
-    public List<Dependency> getGeneratedDependencies() {
-        if (configurations == null) {
-            return Collections.emptyList();
-        }
-        return pomDependenciesConverter.convert(getScopeMappings(), configurations.getAll());
-    }
-
-    public DefaultMavenPom getEffectivePom() {
-        DefaultMavenPom effectivePom = new DefaultMavenPom(null, this.scopeMappings, pomDependenciesConverter, fileResolver);
-        try {
-            effectivePom.setMavenProject((MavenProject) mavenProject.clone());
-        } catch (CloneNotSupportedException e) {
-            throw new RuntimeException(e);
-        }
-        effectivePom.getDependencies().addAll(getGeneratedDependencies());
-        effectivePom.withXmlActions = withXmlActions;
-        whenConfiguredActions.execute(effectivePom);
-        return effectivePom;
-    }
-
-    public PomDependenciesConverter getPomDependenciesConverter() {
-        return pomDependenciesConverter;
-    }
-
-    public FileResolver getFileResolver() {
-        return fileResolver;
-    }
-
-    public DefaultMavenPom setFileResolver(FileResolver fileResolver) {
-        this.fileResolver = fileResolver;
-        return this;
-    }
-
-    public DefaultMavenPom writeTo(final Writer pomWriter) {
-        getEffectivePom().writeNonEffectivePom(pomWriter);
-        return this;
-    }
-
-    public DefaultMavenPom writeTo(Object path) {
-        OutputStream stream = null;
-
-        try {
-            File file = fileResolver.resolve(path);
-            if (file.getParentFile() != null) {
-                file.getParentFile().mkdirs();
-            }
-            stream = new FileOutputStream(file);
-            getEffectivePom().writeNonEffectivePom(stream);
-            return this;
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        } finally {
-            IOUtils.closeQuietly(stream);
-        }
-    }
-
-    private void writeNonEffectivePom(final Writer pomWriter) {
-        try {
-            final StringWriter stringWriter = new StringWriter();
-            mavenProject.writeModel(stringWriter);
-            withXmlActions.transform(stringWriter.toString(), pomWriter);
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        } finally {
-            IOUtils.closeQuietly(pomWriter);
-        }
-    }
-
-    private void writeNonEffectivePom(OutputStream stream) {
-        try {
-            final StringWriter stringWriter = new StringWriter();
-            mavenProject.writeModel(stringWriter);
-            withXmlActions.transform(stringWriter.toString(), stream);
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        } finally {
-            IOUtils.closeQuietly(stream);
-        }
-    }
-
-    public DefaultMavenPom whenConfigured(final Closure closure) {
-        whenConfiguredActions.add(closure);
-        return this;
-    }
-
-    public DefaultMavenPom whenConfigured(final Action<MavenPom> action) {
-        whenConfiguredActions.add(action);
-        return this;
-    }
-
-    public DefaultMavenPom withXml(final Closure closure) {
-        withXmlActions.addAction(closure);
-        return this;
-    }
-
-    public DefaultMavenPom withXml(final Action<XmlProvider> action) {
-        withXmlActions.addAction(action);
-        return this;
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomFactory.java
deleted file mode 100644
index fdf2b7d..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomFactory.java
+++ /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;
-
-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;
-
-/**
- * @author Hans Dockter
- */
-public class DefaultMavenPomFactory implements Factory<MavenPom> {
-    private ConfigurationContainer configurationContainer;
-    private Conf2ScopeMappingContainer conf2ScopeMappingContainer;
-    private PomDependenciesConverter pomDependenciesConverter;
-    private FileResolver fileResolver;
-
-
-    public DefaultMavenPomFactory(ConfigurationContainer configurationContainer, Conf2ScopeMappingContainer conf2ScopeMappingContainer, PomDependenciesConverter pomDependenciesConverter,
-                                  FileResolver fileResolver) {
-        this.configurationContainer = configurationContainer;
-        this.conf2ScopeMappingContainer = conf2ScopeMappingContainer;
-        this.pomDependenciesConverter = pomDependenciesConverter;
-        this.fileResolver = fileResolver;
-    }
-
-    public MavenPom create() {
-        return new DefaultMavenPom(configurationContainer,
-                new DefaultConf2ScopeMappingContainer(conf2ScopeMappingContainer.getMappings()), pomDependenciesConverter, fileResolver);
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultExcludeRuleConverter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultExcludeRuleConverter.java
deleted file mode 100644
index c3704bb..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultExcludeRuleConverter.java
+++ /dev/null
@@ -1,39 +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.dependencies;
-
-import org.apache.maven.model.Exclusion;
-import org.gradle.api.artifacts.ExcludeRule;
-
-
-/**
- * @author Hans Dockter
- */
-public class DefaultExcludeRuleConverter implements ExcludeRuleConverter {
-    public Exclusion convert(ExcludeRule excludeRule) {
-        if (isConvertable(excludeRule)) {
-            Exclusion exclusion = new Exclusion();
-            exclusion.setGroupId(excludeRule.getExcludeArgs().get(ExcludeRule.GROUP_KEY));
-            exclusion.setArtifactId(excludeRule.getExcludeArgs().get(ExcludeRule.MODULE_KEY));
-            return exclusion;
-        }
-        return null;
-    }
-
-    private boolean isConvertable(ExcludeRule excludeRule) {
-        return excludeRule.getExcludeArgs().containsKey(ExcludeRule.GROUP_KEY) && excludeRule.getExcludeArgs().containsKey(ExcludeRule.MODULE_KEY);
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultPomDependenciesConverter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultPomDependenciesConverter.java
deleted file mode 100644
index 2659d09..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultPomDependenciesConverter.java
+++ /dev/null
@@ -1,147 +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.artifacts.publish.maven.dependencies;
-
-import org.apache.maven.model.Dependency;
-import org.apache.maven.model.Exclusion;
-import org.gradle.api.GradleException;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.DependencyArtifact;
-import org.gradle.api.artifacts.ExcludeRule;
-import org.gradle.api.artifacts.ModuleDependency;
-import org.gradle.api.artifacts.maven.Conf2ScopeMapping;
-import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
-
-import java.util.*;
-
-/**
- * @author Hans Dockter
- */
-public class DefaultPomDependenciesConverter implements PomDependenciesConverter {
-    private ExcludeRuleConverter excludeRuleConverter;
-
-    public DefaultPomDependenciesConverter(ExcludeRuleConverter excludeRuleConverter) {
-        this.excludeRuleConverter = excludeRuleConverter;
-    }
-
-    public List<org.apache.maven.model.Dependency> convert(Conf2ScopeMappingContainer conf2ScopeMappingContainer, Set<Configuration> configurations) {
-        Map<ModuleDependency, Set<Configuration>> dependencyToConfigurations = createDependencyToConfigurationsMap(configurations);
-        Map<ModuleDependency, String> dependenciesMap = createDependencyToScopeMap(conf2ScopeMappingContainer, dependencyToConfigurations);
-        List<org.apache.maven.model.Dependency> mavenDependencies = new ArrayList<org.apache.maven.model.Dependency>();
-        for (ModuleDependency dependency : dependenciesMap.keySet()) {
-            if (dependency.getArtifacts().size() == 0) {
-                addFromDependencyDescriptor(mavenDependencies, dependency, dependenciesMap.get(dependency), dependencyToConfigurations.get(dependency));
-            } else {
-                addFromArtifactDescriptor(mavenDependencies, dependency, dependenciesMap.get(dependency), dependencyToConfigurations.get(dependency));
-            }
-        }
-        return mavenDependencies;
-    }
-    
-    private Map<ModuleDependency, String> createDependencyToScopeMap(Conf2ScopeMappingContainer conf2ScopeMappingContainer, 
-            Map<ModuleDependency, Set<Configuration>> dependencyToConfigurations) {
-        Map<ModuleDependency, String> dependencyToScope = new HashMap<ModuleDependency, String>();
-        for (ModuleDependency dependency : dependencyToConfigurations.keySet()) {
-            Conf2ScopeMapping conf2ScopeDependencyMapping = conf2ScopeMappingContainer.getMapping(dependencyToConfigurations.get(dependency));
-            if (!useScope(conf2ScopeMappingContainer, conf2ScopeDependencyMapping)) {
-                continue;
-            }
-            dependencyToScope.put(findDependency(dependency, conf2ScopeDependencyMapping.getConfiguration()),
-                    conf2ScopeDependencyMapping.getScope());
-        }
-        return dependencyToScope;
-    }
-
-    private ModuleDependency findDependency(ModuleDependency dependency, Configuration configuration) {
-        for (ModuleDependency configurationDependency : configuration.getDependencies(ModuleDependency.class)) {
-            if (dependency.equals(configurationDependency)) {
-                return configurationDependency;
-            }
-        }
-        throw new GradleException("Dependency could not be found. We should never get here!");
-    }
-
-    private boolean useScope(Conf2ScopeMappingContainer conf2ScopeMappingContainer, Conf2ScopeMapping conf2ScopeMapping) {
-        return conf2ScopeMapping.getScope() != null || !conf2ScopeMappingContainer.isSkipUnmappedConfs();
-    }
-
-    private Map<ModuleDependency, Set<Configuration>> createDependencyToConfigurationsMap(Set<Configuration> configurations) {
-        Map<ModuleDependency, Set<Configuration>> dependencySetMap = new HashMap<ModuleDependency, Set<Configuration>>();
-        for (Configuration configuration : configurations) {
-            for (ModuleDependency dependency : configuration.getDependencies(ModuleDependency.class)) {
-                if (dependencySetMap.get(dependency) == null) {
-                    dependencySetMap.put(dependency, new HashSet<Configuration>());
-                }
-                dependencySetMap.get(dependency).add(configuration);
-            }
-        }
-        return dependencySetMap;
-    }
-
-    private void addFromArtifactDescriptor(List<Dependency> mavenDependencies, ModuleDependency dependency, String scope, 
-            Set<Configuration> configurations) {
-        for (DependencyArtifact artifact : dependency.getArtifacts()) {
-            mavenDependencies.add(createMavenDependencyFromArtifactDescriptor(dependency, artifact, scope, configurations));
-        }
-    }
-
-    private void addFromDependencyDescriptor(List<Dependency> mavenDependencies, ModuleDependency dependency, String scope, 
-            Set<Configuration> configurations) {
-        mavenDependencies.add(createMavenDependencyFromDependencyDescriptor(dependency, scope, configurations));
-    }
-
-    private Dependency createMavenDependencyFromArtifactDescriptor(ModuleDependency dependency, DependencyArtifact artifact, String scope,
-            Set<Configuration> configurations) {
-        return createMavenDependency(dependency, artifact.getName(), artifact.getType(), scope, artifact.getClassifier(), configurations);
-    }
-
-    private Dependency createMavenDependencyFromDependencyDescriptor(ModuleDependency dependency, String scope, Set<Configuration> configurations) {
-        return createMavenDependency(dependency, dependency.getName(), null, scope, null, configurations);
-    }
-
-    private Dependency createMavenDependency(ModuleDependency dependency, String name, String type, String scope, String classifier,
-            Set<Configuration> configurations) {
-        Dependency mavenDependency =  new Dependency();
-        mavenDependency.setGroupId(dependency.getGroup());
-        mavenDependency.setArtifactId(name);
-        mavenDependency.setVersion(dependency.getVersion());
-        mavenDependency.setType(type);
-        mavenDependency.setScope(scope);
-        mavenDependency.setOptional(false);
-        mavenDependency.setClassifier(classifier);
-        mavenDependency.setExclusions(getExclusions(dependency, configurations));
-        return mavenDependency;
-    }
-
-    private List<Exclusion> getExclusions(ModuleDependency dependency, Set<Configuration> configurations) {
-        List<Exclusion> mavenExclusions = new ArrayList<Exclusion>();
-        Set<ExcludeRule> excludeRules = new HashSet<ExcludeRule>(dependency.getExcludeRules());
-        for (Configuration configuration : configurations) {
-            excludeRules.addAll(configuration.getExcludeRules());
-        }
-        for (ExcludeRule excludeRule : excludeRules) {
-            Exclusion mavenExclusion = excludeRuleConverter.convert(excludeRule);
-            if (mavenExclusion != null) {
-                mavenExclusions.add(mavenExclusion);
-            }
-        }
-        return mavenExclusions;
-    }
-
-    public ExcludeRuleConverter getExcludeRuleConverter() {
-        return excludeRuleConverter;
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/ExcludeRuleConverter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/ExcludeRuleConverter.java
deleted file mode 100644
index 78c247b..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/ExcludeRuleConverter.java
+++ /dev/null
@@ -1,27 +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.dependencies;
-
-import org.apache.maven.model.Exclusion;
-import org.gradle.api.artifacts.ExcludeRule;
-
-
-/**
- * @author Hans Dockter
- */
-public interface ExcludeRuleConverter {
-    Exclusion convert(ExcludeRule excludeRule);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/PomDependenciesConverter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/PomDependenciesConverter.java
deleted file mode 100644
index 43cb885..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/PomDependenciesConverter.java
+++ /dev/null
@@ -1,29 +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.dependencies;
-
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
-
-import java.util.List;
-import java.util.Set;
-
-/**
- * @author Hans Dockter
- */
-public interface PomDependenciesConverter {
-    public List<org.apache.maven.model.Dependency> convert(Conf2ScopeMappingContainer conf2ScopeMappingContainer, Set<Configuration> configurations);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/PomDependenciesWriter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/PomDependenciesWriter.java
deleted file mode 100644
index da126ac..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/PomDependenciesWriter.java
+++ /dev/null
@@ -1,31 +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.dependencies;
-
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.maven.MavenPom;
-
-import java.io.PrintWriter;
-import java.util.Set;
-
-/**
- * @author Hans Dockter
- */
-public interface PomDependenciesWriter {
-    String DEPENDENCIES = "dependencies";
-
-    void convert(MavenPom pom, Set<Configuration> configurations, PrintWriter printWriter);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/AbstractMavenResolver.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/AbstractMavenResolver.java
deleted file mode 100644
index b886907..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/AbstractMavenResolver.java
+++ /dev/null
@@ -1,304 +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.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;
-import org.apache.ivy.core.cache.RepositoryCacheManager;
-import org.apache.ivy.core.module.descriptor.Artifact;
-import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.apache.ivy.core.report.ArtifactDownloadReport;
-import org.apache.ivy.core.report.DownloadReport;
-import org.apache.ivy.core.resolve.DownloadOptions;
-import org.apache.ivy.core.resolve.ResolveData;
-import org.apache.ivy.core.resolve.ResolvedModuleRevision;
-import org.apache.ivy.core.search.ModuleEntry;
-import org.apache.ivy.core.search.OrganisationEntry;
-import org.apache.ivy.core.search.RevisionEntry;
-import org.apache.ivy.plugins.namespace.Namespace;
-import org.apache.ivy.plugins.resolver.ResolverSettings;
-import org.apache.ivy.plugins.resolver.util.ResolvedResource;
-import org.apache.maven.artifact.ant.AttachedArtifact;
-import org.apache.maven.artifact.ant.InstallDeployTaskSupport;
-import org.apache.maven.artifact.ant.Pom;
-import org.apache.maven.settings.Settings;
-import org.apache.tools.ant.Project;
-import org.gradle.api.Action;
-import org.gradle.api.UncheckedIOException;
-import org.gradle.api.artifacts.PublishArtifact;
-import org.gradle.api.artifacts.maven.*;
-import org.gradle.api.logging.LogLevel;
-import org.gradle.listener.ActionBroadcast;
-import org.gradle.logging.LoggingManagerInternal;
-import org.gradle.util.AntUtil;
-
-import java.io.File;
-import java.io.IOException;
-import java.text.ParseException;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * @author Hans Dockter
- */
-public abstract class AbstractMavenResolver implements MavenResolver {
-    protected final static String SETTINGS_XML = "<settings/>"; 
-
-    private String name;
-    
-    private ArtifactPomContainer artifactPomContainer;
-
-    private PomFilterContainer pomFilterContainer;
-
-    private Settings settings;
-
-    private LoggingManagerInternal loggingManager;
-
-    private final ActionBroadcast<MavenDeployment> beforeDeploymentActions = new ActionBroadcast<MavenDeployment>();
-
-    public AbstractMavenResolver(String name, PomFilterContainer pomFilterContainer, ArtifactPomContainer artifactPomContainer, LoggingManagerInternal loggingManager) {
-        this.name = name;
-        this.pomFilterContainer = pomFilterContainer;
-        this.artifactPomContainer = artifactPomContainer;
-        this.loggingManager = loggingManager;
-    }
-
-    protected abstract InstallDeployTaskSupport createPreConfiguredTask(Project project);
-
-    public String getName() {
-        return name;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-    
-    public ResolvedModuleRevision getDependency(DependencyDescriptor dd, ResolveData data) throws ParseException {
-        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
-    }
-
-    public ResolvedResource findIvyFileRef(DependencyDescriptor dd, ResolveData data) {
-        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
-    }
-
-    public DownloadReport download(Artifact[] artifacts, DownloadOptions options) {
-        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
-    }
-
-    public ArtifactDownloadReport download(ArtifactOrigin artifact, DownloadOptions options) {
-        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
-    }
-
-    public boolean exists(Artifact artifact) {
-        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
-    }
-
-    public ArtifactOrigin locate(Artifact artifact) {
-        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
-    }
-
-    public void reportFailure() {
-        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
-    }
-
-    public void reportFailure(Artifact art) {
-        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
-    }
-
-    public String[] listTokenValues(String token, Map otherTokenValues) {
-        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
-    }
-
-    public Map[] listTokenValues(String[] tokens, Map criteria) {
-        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
-    }
-
-    public OrganisationEntry[] listOrganisations() {
-        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
-    }
-
-    public ModuleEntry[] listModules(OrganisationEntry org) {
-        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
-    }
-
-    public RevisionEntry[] listRevisions(ModuleEntry module) {
-        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
-    }
-
-    public Namespace getNamespace() {
-        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
-    }
-
-    public void dumpSettings() {
-        throw new UnsupportedOperationException("A MavenPublishOnlyResolver can only publish artifacts.");
-    }
-
-
-    public void publish(Artifact artifact, File src, boolean overwrite) throws IOException {
-        if (isIgnorable(artifact)) {
-            return;
-        }
-        getArtifactPomContainer().addArtifact(artifact, src);
-    }
-
-    private boolean isIgnorable(Artifact artifact) {
-        return artifact.getType().equals("ivy");
-    }
-
-    public void beginPublishTransaction(ModuleRevisionId module, boolean overwrite) throws IOException {
-        // do nothing
-    }
-
-    public void abortPublishTransaction() throws IOException {
-        // do nothing
-    }
-
-    public void commitPublishTransaction() throws IOException {
-        InstallDeployTaskSupport installDeployTaskSupport = createPreConfiguredTask(AntUtil.createProject());
-        Set<DefaultMavenDeployment> defaultMavenDeployments = getArtifactPomContainer().createDeployableFilesInfos();
-        File emptySettingsXml = createEmptyMavenSettingsXml();
-        installDeployTaskSupport.setSettingsFile(emptySettingsXml);
-        for (DefaultMavenDeployment defaultMavenDeployment : defaultMavenDeployments) {
-            beforeDeploymentActions.execute(defaultMavenDeployment);
-            addPomAndArtifact(installDeployTaskSupport, defaultMavenDeployment);
-            execute(installDeployTaskSupport);
-        }
-        emptySettingsXml.delete();
-        settings = ((CustomInstallDeployTaskSupport) installDeployTaskSupport).getSettings();
-    }
-
-    private void execute(InstallDeployTaskSupport deployTask) {
-        loggingManager.captureStandardOutput(LogLevel.INFO).start();
-        try {
-            deployTask.execute();
-        } finally {
-            loggingManager.stop();
-        }
-    }
-
-    private void addPomAndArtifact(InstallDeployTaskSupport installOrDeployTask, DefaultMavenDeployment defaultMavenDeployment) {
-        Pom pom = new Pom();
-        pom.setProject(installOrDeployTask.getProject());
-        pom.setFile(defaultMavenDeployment.getPomArtifact().getFile());
-        installOrDeployTask.addPom(pom);
-        if (defaultMavenDeployment.getMainArtifact() != null) {
-            installOrDeployTask.setFile(defaultMavenDeployment.getMainArtifact().getFile());
-        }
-        for (PublishArtifact classifierArtifact : defaultMavenDeployment.getAttachedArtifacts()) {
-            AttachedArtifact attachedArtifact = installOrDeployTask.createAttach();
-            attachedArtifact.setClassifier(classifierArtifact.getClassifier());
-            attachedArtifact.setFile(classifierArtifact.getFile());
-            attachedArtifact.setType(classifierArtifact.getType());
-        }
-    }
-
-    private File createEmptyMavenSettingsXml() {
-        try {
-            File settingsXml = File.createTempFile("gradle_empty_settings", ".xml");
-            FileUtils.writeStringToFile(settingsXml, SETTINGS_XML);
-            return settingsXml;
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
-    }
-
-    public void setSettings(ResolverSettings settings) {
-        // do nothing
-    }
-
-    public RepositoryCacheManager getRepositoryCacheManager() {
-        return new DefaultRepositoryCacheManager();
-    }
-
-    public ArtifactPomContainer getArtifactPomContainer() {
-        return artifactPomContainer;
-    }
-
-    public void setArtifactPomContainer(ArtifactPomContainer artifactPomContainer) {
-        this.artifactPomContainer = artifactPomContainer;
-    }
-
-    public Settings getSettings() {
-        return settings;
-    }
-
-    public PublishFilter getFilter() {
-        return pomFilterContainer.getFilter();
-    }
-
-    public void setFilter(PublishFilter defaultFilter) {
-        pomFilterContainer.setFilter(defaultFilter);
-    }
-
-    public MavenPom getPom() {
-        return pomFilterContainer.getPom();
-    }
-
-    public void setPom(MavenPom defaultPom) {
-        pomFilterContainer.setPom(defaultPom);
-    }
-
-    public MavenPom addFilter(String name, PublishFilter publishFilter) {
-        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);
-    }
-
-    public MavenPom pom(String name) {
-        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();
-    }
-
-    public PomFilterContainer getPomFilterContainer() {
-        return pomFilterContainer;
-    }
-
-    public void setPomFilterContainer(PomFilterContainer pomFilterContainer) {
-        this.pomFilterContainer = pomFilterContainer;
-    }
-
-    public void beforeDeployment(Action<? super MavenDeployment> action) {
-        beforeDeploymentActions.add(action);
-    }
-
-    public void beforeDeployment(Closure action) {
-        beforeDeploymentActions.add(action);
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/ArtifactPom.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/ArtifactPom.java
deleted file mode 100644
index 73a882b..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/ArtifactPom.java
+++ /dev/null
@@ -1,41 +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;
-
-import org.apache.ivy.core.module.descriptor.Artifact;
-import org.gradle.api.artifacts.PublishArtifact;
-import org.gradle.api.artifacts.maven.MavenPom;
-
-import java.io.File;
-import java.util.Set;
-
-/**
- * @author Hans Dockter
- */
-public interface ArtifactPom {
-    /**
-     * @return The main artifact, may be null.
-     */
-    PublishArtifact getArtifact();
-
-    MavenPom getPom();
-
-    void addArtifact(Artifact artifact, File src);
-
-    Set<PublishArtifact> getAttachedArtifacts();
-
-    PublishArtifact writePom(File pomFile);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/ArtifactPomContainer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/ArtifactPomContainer.java
deleted file mode 100644
index 0feb1ae..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/ArtifactPomContainer.java
+++ /dev/null
@@ -1,30 +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;
-
-import org.apache.ivy.core.module.descriptor.Artifact;
-
-import java.io.File;
-import java.util.Set;
-
-/**
- * @author Hans Dockter
- */
-public interface ArtifactPomContainer {
-    void addArtifact(Artifact artifact, File src);
-
-    Set<DefaultMavenDeployment> createDeployableFilesInfos();
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/ArtifactPomFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/ArtifactPomFactory.java
deleted file mode 100644
index 4efdc58..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/ArtifactPomFactory.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.deploy;
-
-import org.gradle.api.artifacts.maven.MavenPom;
-
-/**
- * @author Hans Dockter
- */
-public interface ArtifactPomFactory {
-    ArtifactPom createArtifactPom(MavenPom pom);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenDeployer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenDeployer.java
deleted file mode 100644
index 2169ed6..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenDeployer.java
+++ /dev/null
@@ -1,128 +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;
-
-import org.apache.maven.artifact.ant.DeployTask;
-import org.apache.maven.artifact.ant.InstallDeployTaskSupport;
-import org.apache.maven.artifact.ant.RemoteRepository;
-import org.apache.tools.ant.Project;
-import org.codehaus.plexus.PlexusContainer;
-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;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * @author Hans Dockter
- */
-public class BaseMavenDeployer extends AbstractMavenResolver implements MavenDeployer {
-    private RemoteRepository remoteRepository;
-
-    private RemoteRepository remoteSnapshotRepository;
-
-    private Factory<CustomDeployTask> deployTaskFactory = new DefaultDeployTaskFactory();
-
-    private Configuration configuration;
-
-    // todo remove this property once configuration can handle normal file system dependencies
-    private List<File> protocolProviderJars = new ArrayList<File>();
-
-    private boolean uniqueVersion = true;
-
-    public BaseMavenDeployer(String name, PomFilterContainer pomFilterContainer, ArtifactPomContainer artifactPomContainer, LoggingManagerInternal loggingManager) {
-        super(name, pomFilterContainer, artifactPomContainer, loggingManager);
-    }
-
-    protected InstallDeployTaskSupport createPreConfiguredTask(Project project) {
-        CustomDeployTask deployTask = deployTaskFactory.create();
-        deployTask.setProject(project);
-        deployTask.setUniqueVersion(isUniqueVersion());
-        addProtocolProvider(deployTask);
-        addRemoteRepositories(deployTask);
-        return deployTask;
-    }
-
-    private void addProtocolProvider(CustomDeployTask deployTask) {
-        PlexusContainer plexusContainer = deployTask.getContainer();
-        for (File wagonProviderJar : getJars()) {
-            try {
-                plexusContainer.addJarResource(wagonProviderJar);
-            } catch (PlexusContainerException e) {
-                throw new RuntimeException(e);
-            }
-        }
-    }
-
-    private List<File> getJars() {
-        return configuration != null ? new ArrayList(configuration.resolve()) : protocolProviderJars;
-    }
-
-    private void addRemoteRepositories(DeployTask deployTask) {
-        deployTask.addRemoteRepository(remoteRepository);
-        deployTask.addRemoteSnapshotRepository(remoteSnapshotRepository);
-    }
-
-    public RemoteRepository getRepository() {
-        return remoteRepository;
-    }
-
-    public void setRepository(RemoteRepository remoteRepository) {
-        this.remoteRepository = remoteRepository;
-    }
-
-    public RemoteRepository getSnapshotRepository() {
-        return remoteSnapshotRepository;
-    }
-
-    public void setSnapshotRepository(RemoteRepository remoteSnapshotRepository) {
-        this.remoteSnapshotRepository = remoteSnapshotRepository;
-    }
-
-    public Factory<CustomDeployTask> getDeployTaskFactory() {
-        return deployTaskFactory;
-    }
-
-    public void setDeployTaskFactory(Factory<CustomDeployTask> deployTaskFactory) {
-        this.deployTaskFactory = deployTaskFactory;
-    }
-
-    public void addProtocolProviderJars(Collection<File> jars) {
-        protocolProviderJars.addAll(jars);
-    }
-
-    public Configuration getConfiguration() {
-        return configuration;
-    }
-
-    public void setConfiguration(Configuration configuration) {
-        this.configuration = configuration;
-    }
-
-    public boolean isUniqueVersion() {
-        return uniqueVersion;
-    }
-
-    public void setUniqueVersion(boolean uniqueVersion) {
-        this.uniqueVersion = uniqueVersion;
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenInstaller.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenInstaller.java
deleted file mode 100644
index 69e18bc..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenInstaller.java
+++ /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;
-
-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 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.create();
-        installTask.setProject(project);
-        return installTask;
-    }
-
-    public Factory<CustomInstallTask> getInstallTaskFactory() {
-        return installTaskFactory;
-    }
-
-    public void setInstallTaskFactory(Factory<CustomInstallTask> installTaskFactory) {
-        this.installTaskFactory = installTaskFactory;
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BasePomFilterContainer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BasePomFilterContainer.java
deleted file mode 100644
index f11bc92..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BasePomFilterContainer.java
+++ /dev/null
@@ -1,137 +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.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.Factory;
-import org.gradle.util.ConfigureUtil;
-import org.gradle.util.WrapUtil;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * @author Hans Dockter
- */
-public class BasePomFilterContainer implements PomFilterContainer {
-    private Map<String, PomFilter> pomFilters = new HashMap<String, PomFilter>();
-
-    private PomFilter defaultPomFilter;
-
-    private Factory<? extends MavenPom> mavenPomFactory;
-
-    public BasePomFilterContainer(Factory<? extends MavenPom> mavenPomFactory) {
-        this.mavenPomFactory = mavenPomFactory;
-    }
-
-    public PublishFilter getFilter() {
-        return getDefaultPomFilter().getFilter();
-    }
-
-    public void setFilter(PublishFilter defaultFilter) {
-        getDefaultPomFilter().setFilter(defaultFilter);
-    }
-
-    public MavenPom getPom() {
-        return getDefaultPomFilter().getPomTemplate();
-    }
-
-    public void setPom(MavenPom defaultPom) {
-        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.create();
-        pomFilters.put(name, new DefaultPomFilter(name, pom, publishFilter));
-        return pom;
-    }
-
-    public PublishFilter filter(String name) {
-        if (name == null) {
-            throw new InvalidUserDataException("Name must not be null.");
-        }
-        return pomFilters.get(name).getFilter();
-    }
-
-    public MavenPom pom(String name) {
-        if (name == null) {
-            throw new InvalidUserDataException("Name must not be null.");
-        }
-        return pomFilters.get(name).getPomTemplate();
-    }
-
-    public Iterable<PomFilter> getActivePomFilters() {
-        Iterable<PomFilter> activeArtifactPoms;
-        if (pomFilters.size() == 0 && getDefaultPomFilter() != null) {
-            activeArtifactPoms = WrapUtil.toSet(getDefaultPomFilter());
-        } else {
-            activeArtifactPoms = pomFilters.values();
-        }
-        return activeArtifactPoms;
-    }
-
-    public Factory<? extends MavenPom> getMavenPomFactory() {
-        return mavenPomFactory;
-    }
-
-    public PomFilter getDefaultPomFilter() {
-        if (defaultPomFilter == null) {
-            defaultPomFilter = new DefaultPomFilter(PomFilterContainer.DEFAULT_ARTIFACT_POM_NAME, mavenPomFactory.create(),
-                PublishFilter.ALWAYS_ACCEPT);
-        }
-        return defaultPomFilter;
-    }
-
-    public void setDefaultPomFilter(PomFilter defaultPomFilter) {
-        this.defaultPomFilter = defaultPomFilter;
-    }
-
-    public Map<String, PomFilter> getPomFilters() {
-        return pomFilters;
-    }
-
-    protected BasePomFilterContainer newInstance() {
-        return new BasePomFilterContainer(mavenPomFactory);
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultArtifactPom.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultArtifactPom.java
deleted file mode 100644
index 53d06ad..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultArtifactPom.java
+++ /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.api.internal.artifacts.publish.maven.deploy;
-
-import com.google.common.collect.Sets;
-import org.apache.commons.lang.ObjectUtils;
-import org.apache.ivy.core.module.descriptor.Artifact;
-import org.apache.maven.project.MavenProject;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.artifacts.Dependency;
-import org.gradle.api.artifacts.PublishArtifact;
-import org.gradle.api.artifacts.maven.MavenPom;
-import org.gradle.api.internal.artifacts.publish.AbstractPublishArtifact;
-
-import java.io.File;
-import java.util.*;
-
-/**
- * @author Hans Dockter
- */
-public class DefaultArtifactPom implements ArtifactPom {
-    private static final Set<String> PACKAGING_TYPES = Sets.newHashSet("war", "jar", "ear");
-    private final MavenPom pom;
-    private final Map<ArtifactKey, PublishArtifact> artifacts = new HashMap<ArtifactKey, PublishArtifact>();
-
-    private PublishArtifact artifact;
-
-    private final Set<PublishArtifact> classifiers = new HashSet<PublishArtifact>();
-
-    public DefaultArtifactPom(MavenPom pom) {
-        this.pom = pom;
-    }
-
-    public MavenPom getPom() {
-        return pom;
-    }
-
-    public PublishArtifact getArtifact() {
-        return artifact;
-    }
-
-    public Set<PublishArtifact> getAttachedArtifacts() {
-        return Collections.unmodifiableSet(classifiers);
-    }
-
-    public PublishArtifact writePom(final File pomFile) {
-        getPom().writeTo(pomFile);
-        return new PomArtifact(pomFile);
-    }
-
-    public void addArtifact(Artifact artifact, File src) {
-        throwExceptionIfArtifactOrSrcIsNull(artifact, src);
-        PublishArtifact publishArtifact = new MavenArtifact(artifact, src);
-        ArtifactKey artifactKey = new ArtifactKey(publishArtifact);
-        if (this.artifacts.containsKey(artifactKey)) {
-            throw new InvalidUserDataException(String.format("A POM cannot have multiple artifacts with the same type and classifier. Already have %s, trying to add %s.", this.artifacts.get(
-                    artifactKey), publishArtifact));
-        }
-
-        if (publishArtifact.getClassifier() != null) {
-            addArtifact(publishArtifact);
-            assignArtifactValuesToPom(artifact, pom, false);
-            return;
-        }
-
-        if (this.artifact != null) {
-            // Choose the 'main' artifact based on its type.
-            if (!PACKAGING_TYPES.contains(artifact.getType())) {
-                addArtifact(publishArtifact);
-                return;
-            }
-            if (PACKAGING_TYPES.contains(this.artifact.getType())) {
-                throw new InvalidUserDataException("A POM can not have multiple main artifacts. " + "Already have " + this.artifact + ", trying to add " + publishArtifact);
-            }
-            addArtifact(this.artifact);
-        }
-
-        this.artifact = publishArtifact;
-        this.artifacts.put(artifactKey, publishArtifact);
-        assignArtifactValuesToPom(artifact, pom, true);
-    }
-
-    private void addArtifact(PublishArtifact artifact) {
-        classifiers.add(artifact);
-        artifacts.put(new ArtifactKey(artifact), artifact);
-    }
-
-    private String getClassifier(Artifact artifact) {
-        return artifact.getExtraAttribute(Dependency.CLASSIFIER);
-    }
-
-    private void assignArtifactValuesToPom(Artifact artifact, MavenPom pom, boolean setType) {
-        if (pom.getGroupId().equals(MavenProject.EMPTY_PROJECT_GROUP_ID)) {
-            pom.setGroupId(artifact.getModuleRevisionId().getOrganisation());
-        }
-        if (pom.getArtifactId().equals(MavenProject.EMPTY_PROJECT_ARTIFACT_ID)) {
-            pom.setArtifactId(artifact.getName());
-        }
-        if (pom.getVersion().equals(MavenProject.EMPTY_PROJECT_VERSION)) {
-            pom.setVersion(artifact.getModuleRevisionId().getRevision());
-        }
-        if (setType) {
-            pom.setPackaging(artifact.getType());
-        }
-    }
-
-    private void throwExceptionIfArtifactOrSrcIsNull(Artifact artifact, File src) {
-        if (artifact == null) {
-            throw new InvalidUserDataException("Artifact must not be null.");
-        }
-        if (src == null) {
-            throw new InvalidUserDataException("Src file must not be null.");
-        }
-    }
-
-    private static class ArtifactKey {
-        private final String type;
-        private final String classifier;
-
-        private ArtifactKey(PublishArtifact artifact) {
-            this.type = artifact.getType();
-            this.classifier = artifact.getClassifier();
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            ArtifactKey other = (ArtifactKey) o;
-            return ObjectUtils.equals(type, other.type) && ObjectUtils.equals(classifier, other.classifier);
-        }
-
-        @Override
-        public int hashCode() {
-            return ObjectUtils.hashCode(type) ^ ObjectUtils.hashCode(classifier);
-        }
-    }
-
-    private abstract class AbstractMavenArtifact extends AbstractPublishArtifact {
-        private final File file;
-
-        protected AbstractMavenArtifact(File file) {
-            this.file = file;
-        }
-
-        public File getFile() {
-            return file;
-        }
-
-        public String getName() {
-            return pom.getArtifactId();
-        }
-
-        public Date getDate() {
-            return null;
-        }
-    }
-
-    private class MavenArtifact extends AbstractMavenArtifact {
-        private final Artifact artifact;
-
-        private MavenArtifact(Artifact artifact, File file) {
-            super(file);
-            this.artifact = artifact;
-        }
-
-        public String getClassifier() {
-            return DefaultArtifactPom.this.getClassifier(artifact);
-        }
-
-        public String getExtension() {
-            return artifact.getExt();
-        }
-
-        public String getType() {
-            return artifact.getType();
-        }
-    }
-
-    private class PomArtifact extends AbstractMavenArtifact {
-        public PomArtifact(File pomFile) {
-            super(pomFile);
-        }
-
-        public String getExtension() {
-            return "pom";
-        }
-
-        public String getType() {
-            return "pom";
-        }
-
-        public String getClassifier() {
-            return null;
-        }
-
-        public Date getDate() {
-            return null;
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultArtifactPomContainer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultArtifactPomContainer.java
deleted file mode 100644
index 45767d5..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultArtifactPomContainer.java
+++ /dev/null
@@ -1,74 +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;
-
-import org.apache.ivy.core.module.descriptor.Artifact;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.artifacts.PublishArtifact;
-import org.gradle.api.artifacts.maven.PomFilterContainer;
-import org.gradle.api.internal.artifacts.publish.maven.MavenPomMetaInfoProvider;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * @author Hans Dockter
- */
-public class DefaultArtifactPomContainer implements ArtifactPomContainer {
-    private Map<String, ArtifactPom> artifactPoms = new HashMap<String, ArtifactPom>();
-    private final MavenPomMetaInfoProvider pomMetaInfoProvider;
-    private PomFilterContainer pomFilterContainer;
-    private ArtifactPomFactory artifactPomFactory;
-
-    public DefaultArtifactPomContainer(MavenPomMetaInfoProvider pomMetaInfoProvider, PomFilterContainer pomFilterContainer,
-                                       ArtifactPomFactory artifactPomFactory) {
-        this.pomMetaInfoProvider = pomMetaInfoProvider;
-        this.pomFilterContainer = pomFilterContainer;
-        this.artifactPomFactory = artifactPomFactory;
-    }
-
-    public void addArtifact(Artifact artifact, File src) {
-        if (artifact == null || src == null) {
-            throw new InvalidUserDataException("Artifact or source file must not be null!");
-        }
-        for (PomFilter activePomFilter : pomFilterContainer.getActivePomFilters()) {
-            if (activePomFilter.getFilter().accept(artifact, src)) {
-                if (artifactPoms.get(activePomFilter.getName()) == null) {
-                    artifactPoms.put(activePomFilter.getName(), artifactPomFactory.createArtifactPom(activePomFilter.getPomTemplate()));
-                }
-                artifactPoms.get(activePomFilter.getName()).addArtifact(artifact, src); 
-            }
-        }
-    }
-
-    public Set<DefaultMavenDeployment> createDeployableFilesInfos() {
-        Set<DefaultMavenDeployment> defaultMavenDeployments = new HashSet<DefaultMavenDeployment>();
-        for (String activeArtifactPomName : artifactPoms.keySet()) {
-            ArtifactPom activeArtifactPom = artifactPoms.get(activeArtifactPomName);
-            File pomFile = createPomFile(activeArtifactPomName);
-            PublishArtifact pomArtifact = activeArtifactPom.writePom(pomFile);
-            defaultMavenDeployments.add(new DefaultMavenDeployment(pomArtifact, activeArtifactPom.getArtifact(), activeArtifactPom.getAttachedArtifacts()));
-        }
-        return defaultMavenDeployments;
-    }
-
-    private File createPomFile(String artifactPomName) {
-        return new File(pomMetaInfoProvider.getMavenPomDir(), "pom-" + artifactPomName + ".xml");
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenDeployer.groovy b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenDeployer.groovy
deleted file mode 100644
index 663db2e..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenDeployer.groovy
+++ /dev/null
@@ -1,51 +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.codehaus.groovy.runtime.InvokerHelper
-import org.gradle.api.artifacts.maven.GroovyMavenDeployer
-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
-
-/**
- * @author Hans Dockter
- */
-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, PomFilterContainer pomFilterContainer, ArtifactPomContainer artifactPomContainer, LoggingManagerInternal loggingManager) {
-        super(name, pomFilterContainer, artifactPomContainer, loggingManager)
-    }
-    
-    def methodMissing(String name, args) {
-        if (name == REPOSITORY_BUILDER || name == SNAPSHOT_REPOSITORY_BUILDER) {
-            Object repository = InvokerHelper.invokeMethod(repositoryBuilder, REPOSITORY_BUILDER, args)
-            if (name == REPOSITORY_BUILDER) {
-                setRepository(repository)
-            } else {
-                setSnapshotRepository(repository)
-            }
-            return repository;
-        } else {
-            throw new MissingMethodException(name, this.class, args)
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/repositories/ArtifactRepositoryInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/repositories/ArtifactRepositoryInternal.java
new file mode 100644
index 0000000..08e4c4a
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/repositories/ArtifactRepositoryInternal.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+import org.gradle.api.artifacts.dsl.ArtifactRepository;
+
+import java.util.Collection;
+
+public interface ArtifactRepositoryInternal extends ArtifactRepository {
+    /**
+     * Creates the resolvers for this repository.
+     *
+     * @param resolvers The collection to add the resolvers for this repository.
+     */
+    void createResolvers(Collection<DependencyResolver> resolvers);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultInternalRepository.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultInternalRepository.java
index 5dc24ad..6a2a309 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultInternalRepository.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultInternalRepository.java
@@ -38,6 +38,7 @@ import org.gradle.api.artifacts.Module;
 import org.gradle.api.artifacts.ResolverContainer;
 import org.gradle.api.internal.artifacts.ivyservice.DefaultIvyDependencyPublisher;
 import org.gradle.api.internal.artifacts.ivyservice.ModuleDescriptorConverter;
+import org.gradle.api.internal.artifacts.ivyservice.NoOpRepositoryCacheManager;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DependencyDescriptorFactory;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.invocation.Gradle;
@@ -61,6 +62,7 @@ public class DefaultInternalRepository extends BasicResolver implements Internal
         this.gradle = gradle;
         this.moduleDescriptorConverter = moduleDescriptorConverter;
         setName(ResolverContainer.INTERNAL_REPOSITORY_NAME);
+        setRepositoryCacheManager(new NoOpRepositoryCacheManager(getName()));
     }
 
     @Override
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/CacheBackedFileSnapshotRepository.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/CacheBackedFileSnapshotRepository.java
new file mode 100644
index 0000000..b5cea28
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/CacheBackedFileSnapshotRepository.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection;
+
+import org.gradle.cache.CacheRepository;
+import org.gradle.cache.PersistentIndexedCache;
+
+public class CacheBackedFileSnapshotRepository implements FileSnapshotRepository {
+    private final CacheRepository repository;
+    private PersistentIndexedCache<Object, Object> cache;
+
+    public CacheBackedFileSnapshotRepository(CacheRepository repository) {
+        this.repository = repository;
+    }
+
+    public Long add(FileCollectionSnapshot snapshot) {
+        open();
+        Long id = (Long) cache.get("nextId");
+        if (id == null) {
+            id = 1L;
+        }
+        cache.put("nextId", id + 1);
+        cache.put(id, snapshot);
+        return id;
+    }
+
+    public FileCollectionSnapshot get(Long id) {
+        open();
+        return (FileCollectionSnapshot) cache.get(id);
+    }
+
+    public void remove(Long id) {
+        open();
+        cache.remove(id);
+    }
+
+    private void open() {
+        if (cache == null) {
+            cache = repository.cache("fileSnapshots").open().openIndexedCache();
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/CacheBackedTaskHistoryRepository.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/CacheBackedTaskHistoryRepository.java
new file mode 100644
index 0000000..67678fe
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/CacheBackedTaskHistoryRepository.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection;
+
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.cache.CacheRepository;
+import org.gradle.cache.DefaultSerializer;
+import org.gradle.cache.PersistentIndexedCache;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class CacheBackedTaskHistoryRepository implements TaskHistoryRepository {
+    private final CacheRepository repository;
+    private final FileSnapshotRepository snapshotRepository;
+    private PersistentIndexedCache<String, TaskHistory> taskHistoryCache;
+    private DefaultSerializer<TaskHistory> serializer;
+
+    public CacheBackedTaskHistoryRepository(CacheRepository repository, FileSnapshotRepository snapshotRepository) {
+        this.repository = repository;
+        this.snapshotRepository = snapshotRepository;
+    }
+
+    public History getHistory(final TaskInternal task) {
+        if (taskHistoryCache == null) {
+            serializer = new DefaultSerializer<TaskHistory>();
+            taskHistoryCache = repository.cache("taskArtifacts").forObject(task.getProject().getGradle()).open().openIndexedCache(serializer);
+        }
+        final TaskHistory history = loadHistory(task);
+        final LazyTaskExecution currentExecution = new LazyTaskExecution();
+        currentExecution.snapshotRepository = snapshotRepository;
+        currentExecution.setOutputFiles(outputFiles(task));
+        final LazyTaskExecution previousExecution = findPreviousExecution(currentExecution, history);
+        if (previousExecution != null) {
+            previousExecution.snapshotRepository = snapshotRepository;
+        }
+        history.configurations.add(0, currentExecution);
+
+        return new History() {
+            public TaskExecution getPreviousExecution() {
+                return previousExecution;
+            }
+
+            public TaskExecution getCurrentExecution() {
+                return currentExecution;
+            }
+
+            public void update() {
+                if (currentExecution.inputFilesSnapshotId == null && currentExecution.inputFilesSnapshot != null) {
+                    currentExecution.inputFilesSnapshotId = snapshotRepository.add(currentExecution.inputFilesSnapshot);
+                }
+                if (currentExecution.outputFilesSnapshotId == null && currentExecution.outputFilesSnapshot != null) {
+                    currentExecution.outputFilesSnapshotId = snapshotRepository.add(currentExecution.outputFilesSnapshot);
+                }
+                while (history.configurations.size() > TaskHistory.MAX_HISTORY_ENTRIES) {
+                    LazyTaskExecution execution = history.configurations.remove(history.configurations.size() - 1);
+                    if (execution.inputFilesSnapshotId != null) {
+                        snapshotRepository.remove(execution.inputFilesSnapshotId);
+                    }
+                    if (execution.outputFilesSnapshotId != null) {
+                        snapshotRepository.remove(execution.outputFilesSnapshotId);
+                    }
+                }
+                taskHistoryCache.put(task.getPath(), history);
+            }
+        };
+    }
+
+    private TaskHistory loadHistory(TaskInternal task) {
+        ClassLoader original = serializer.getClassLoader();
+        serializer.setClassLoader(task.getClass().getClassLoader());
+        try {
+            TaskHistory history = taskHistoryCache.get(task.getPath());
+            return history == null ? new TaskHistory() : history;
+        } finally {
+            serializer.setClassLoader(original);
+        }
+    }
+
+    private static Set<String> outputFiles(TaskInternal task) {
+        Set<String> outputFiles = new HashSet<String>();
+        for (File file : task.getOutputs().getFiles()) {
+            outputFiles.add(file.getAbsolutePath());
+        }
+        return outputFiles;
+    }
+
+    private LazyTaskExecution findPreviousExecution(TaskExecution currentExecution, TaskHistory history) {
+        Set<String> outputFiles = currentExecution.getOutputFiles();
+        LazyTaskExecution bestMatch = null;
+        int bestMatchOverlap = 0;
+        for (LazyTaskExecution configuration : history.configurations) {
+            if (outputFiles.size() == 0) {
+                if (configuration.getOutputFiles().size() == 0) {
+                    bestMatch = configuration;
+                    break;
+                }
+            }
+
+            Set<String> intersection = new HashSet<String>(outputFiles);
+            intersection.retainAll(configuration.getOutputFiles());
+            if (intersection.size() > bestMatchOverlap) {
+                bestMatch = configuration;
+                bestMatchOverlap = intersection.size();
+            }
+            if (bestMatchOverlap == outputFiles.size()) {
+                break;
+            }
+        }
+        return bestMatch;
+    }
+
+    private static class TaskHistory implements Serializable {
+        private static final int MAX_HISTORY_ENTRIES = 3;
+        private final List<LazyTaskExecution> configurations = new ArrayList<LazyTaskExecution>();
+    }
+
+    private static class LazyTaskExecution extends TaskExecution {
+        private Long inputFilesSnapshotId;
+        private Long outputFilesSnapshotId;
+        private transient FileSnapshotRepository snapshotRepository;
+        private transient FileCollectionSnapshot inputFilesSnapshot;
+        private transient FileCollectionSnapshot outputFilesSnapshot;
+
+        @Override
+        public FileCollectionSnapshot getInputFilesSnapshot() {
+            if (inputFilesSnapshot == null) {
+                inputFilesSnapshot = snapshotRepository.get(inputFilesSnapshotId);
+            }
+            return inputFilesSnapshot;
+        }
+
+        @Override
+        public void setInputFilesSnapshot(FileCollectionSnapshot inputFilesSnapshot) {
+            this.inputFilesSnapshot = inputFilesSnapshot;
+            this.inputFilesSnapshotId = null;
+        }
+
+        @Override
+        public FileCollectionSnapshot getOutputFilesSnapshot() {
+            if (outputFilesSnapshot == null) {
+                outputFilesSnapshot = snapshotRepository.get(outputFilesSnapshotId);
+            }
+            return outputFilesSnapshot;
+        }
+
+        @Override
+        public void setOutputFilesSnapshot(FileCollectionSnapshot outputFilesSnapshot) {
+            this.outputFilesSnapshot = outputFilesSnapshot;
+            outputFilesSnapshotId = null;
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/CompositeUpToDateRule.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/CompositeUpToDateRule.java
new file mode 100644
index 0000000..d739b3b
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/CompositeUpToDateRule.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection;
+
+import org.gradle.api.internal.TaskInternal;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+public class CompositeUpToDateRule implements UpToDateRule {
+    private final List<UpToDateRule> rules;
+
+    public CompositeUpToDateRule(UpToDateRule... rules) {
+        this.rules = new ArrayList<UpToDateRule>(Arrays.asList(rules));
+    }
+
+    public TaskUpToDateState create(TaskInternal task, TaskExecution previousExecution, TaskExecution currentExecution) {
+        final List<TaskUpToDateState> states = new ArrayList<TaskUpToDateState>();
+        for (UpToDateRule rule : rules) {
+            states.add(rule.create(task, previousExecution, currentExecution));
+        }
+        return new TaskUpToDateState() {
+            public void checkUpToDate(Collection<String> messages) {
+                for (int i = 0; messages.isEmpty() && i < states.size(); i++) {
+                    TaskUpToDateState state = states.get(i);
+                    state.checkUpToDate(messages);
+                }
+            }
+
+            public void snapshotAfterTask() {
+                for (TaskUpToDateState state : states) {
+                    state.snapshotAfterTask();
+                }
+            }
+        };
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/DefaultFileCacheListener.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/DefaultFileCacheListener.java
new file mode 100644
index 0000000..50038d2
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/DefaultFileCacheListener.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.collections.DefaultFileCollectionResolveContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+public class DefaultFileCacheListener implements FileCacheListener {
+    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultFileCacheListener.class);
+
+    public void cacheable(FileCollection files) {
+        List<FileCollection> collections = new DefaultFileCollectionResolveContext().add(files).resolveAsFileCollections();
+        for (FileCollection collection : collections) {
+            LOGGER.debug("Can cache files for {}", collection);
+        }
+    }
+
+    public void invalidate(FileCollection files) {
+        List<FileCollection> collections = new DefaultFileCollectionResolveContext().add(files).resolveAsFileCollections();
+        for (FileCollection collection : collections) {
+            LOGGER.debug("Invalidate cached files for {}", collection);
+        }
+    }
+
+    public void invalidateAll() {
+        LOGGER.debug("Invalidate all cached files");
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/DefaultFileSnapshotter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/DefaultFileSnapshotter.java
index cc4d72b..abd727b 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/DefaultFileSnapshotter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/DefaultFileSnapshotter.java
@@ -1,158 +1,158 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection;
-
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.file.SimpleFileCollection;
-import org.gradle.util.ChangeListener;
-import org.gradle.util.NoOpChangeListener;
-
-import java.io.File;
-import java.io.Serializable;
-import java.math.BigInteger;
-import java.util.*;
-
-public class DefaultFileSnapshotter implements FileSnapshotter {
-    private final Hasher hasher;
-
-    public DefaultFileSnapshotter(Hasher hasher) {
-        this.hasher = hasher;
-    }
-
-    public FileCollectionSnapshot snapshot() {
-        return new FileCollectionSnapshotImpl(new HashMap<String, FileSnapshot>());
-    }
-
-    public FileCollectionSnapshot snapshot(FileCollection sourceFiles) {
-        Map<String, FileSnapshot> snapshots = new HashMap<String, FileSnapshot>();
-        for (File file : sourceFiles.getAsFileTree()) {
-            if (file.isFile()) {
-                snapshots.put(file.getAbsolutePath(), new FileHashSnapshot(hasher.hash(file)));
-            } else if (file.isDirectory()) {
-                snapshots.put(file.getAbsolutePath(), new DirSnapshot());
-            } else {
-                snapshots.put(file.getAbsolutePath(), new MissingFileSnapshot());
-            }
-        }
-        return new FileCollectionSnapshotImpl(snapshots);
-    }
-
-    private interface FileSnapshot extends Serializable {
-        boolean isUpToDate(FileSnapshot snapshot);
-    }
-
-    private static class FileHashSnapshot implements FileSnapshot {
-        private final byte[] hash;
-
-        public FileHashSnapshot(byte[] hash) {
-            this.hash = hash;
-        }
-
-        public boolean isUpToDate(FileSnapshot snapshot) {
-            if (!(snapshot instanceof FileHashSnapshot)) {
-                return false;
-            }
-
-            FileHashSnapshot other = (FileHashSnapshot) snapshot;
-            return Arrays.equals(hash, other.hash);
-        }
-
-        @Override
-        public String toString() {
-            return new BigInteger(1, hash).toString(16);
-        }
-    }
-
-    private static class DirSnapshot implements FileSnapshot {
-        public boolean isUpToDate(FileSnapshot snapshot) {
-            return snapshot instanceof DirSnapshot;
-        }
-    }
-
-    private static class MissingFileSnapshot implements FileSnapshot {
-        public boolean isUpToDate(FileSnapshot snapshot) {
-            return snapshot instanceof MissingFileSnapshot;
-        }
-    }
-
-    private static class FileCollectionSnapshotImpl implements FileCollectionSnapshot {
-        private final Map<String, FileSnapshot> snapshots;
-
-        public FileCollectionSnapshotImpl(Map<String, FileSnapshot> snapshots) {
-            this.snapshots = snapshots;
-        }
-
-        public FileCollection getFiles() {
-            List<File> files = new ArrayList<File>();
-            for (Map.Entry<String, FileSnapshot> entry : snapshots.entrySet()) {
-                if (entry.getValue() instanceof FileHashSnapshot) {
-                    files.add(new File(entry.getKey()));
-                }
-            }
-            return new SimpleFileCollection(files);
-        }
-
-        public void changesSince(FileCollectionSnapshot oldSnapshot, final ChangeListener<File> listener) {
-            FileCollectionSnapshotImpl other = (FileCollectionSnapshotImpl) oldSnapshot;
-            diff(snapshots, other.snapshots, new ChangeListener<Map.Entry<String, FileSnapshot>>() {
-                public void added(Map.Entry<String, FileSnapshot> element) {
-                    listener.added(new File(element.getKey()));
-                }
-
-                public void removed(Map.Entry<String, FileSnapshot> element) {
-                    listener.removed(new File(element.getKey()));
-                }
-
-                public void changed(Map.Entry<String, FileSnapshot> element) {
-                    listener.changed(new File(element.getKey()));
-                }
-            });
-        }
-
-        private void diff(Map<String, FileSnapshot> snapshots, Map<String, FileSnapshot> oldSnapshots,
-                          ChangeListener<Map.Entry<String, FileSnapshot>> listener) {
-            Map<String, FileSnapshot> otherSnapshots = new HashMap<String, FileSnapshot>(oldSnapshots);
-            for (Map.Entry<String, FileSnapshot> entry : snapshots.entrySet()) {
-                FileSnapshot otherFile = otherSnapshots.remove(entry.getKey());
-                if (otherFile == null) {
-                    listener.added(entry);
-                } else if (!entry.getValue().isUpToDate(otherFile)) {
-                    listener.changed(entry);
-                }
-            }
-            for (Map.Entry<String, FileSnapshot> entry : otherSnapshots.entrySet()) {
-                listener.removed(entry);
-            }
-        }
-
-        public Diff changesSince(final FileCollectionSnapshot oldSnapshot) {
-            final FileCollectionSnapshotImpl other = (FileCollectionSnapshotImpl) oldSnapshot;
-            return new Diff() {
-                public FileCollectionSnapshot applyTo(FileCollectionSnapshot snapshot) {
-                    return applyTo(snapshot, new NoOpChangeListener<Merge>());
-                }
-
-                public FileCollectionSnapshot applyTo(FileCollectionSnapshot snapshot, final ChangeListener<Merge> listener) {
-                    FileCollectionSnapshotImpl target = (FileCollectionSnapshotImpl) snapshot;
-                    final Map<String, FileSnapshot> newSnapshots = new HashMap<String, FileSnapshot>(target.snapshots);
-                    diff(snapshots, other.snapshots, new MapMergeChangeListener<String, FileSnapshot>(listener, newSnapshots));
-                    return new FileCollectionSnapshotImpl(newSnapshots);
-                }
-            };
-        }
-    }
-}
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.collections.SimpleFileCollection;
+import org.gradle.util.ChangeListener;
+import org.gradle.util.NoOpChangeListener;
+
+import java.io.File;
+import java.io.Serializable;
+import java.math.BigInteger;
+import java.util.*;
+
+public class DefaultFileSnapshotter implements FileSnapshotter {
+    private final Hasher hasher;
+
+    public DefaultFileSnapshotter(Hasher hasher) {
+        this.hasher = hasher;
+    }
+
+    public FileCollectionSnapshot emptySnapshot() {
+        return new FileCollectionSnapshotImpl(new HashMap<String, FileSnapshot>());
+    }
+
+    public FileCollectionSnapshot snapshot(FileCollection sourceFiles) {
+        Map<String, FileSnapshot> snapshots = new HashMap<String, FileSnapshot>();
+        for (File file : sourceFiles.getAsFileTree()) {
+            if (file.isFile()) {
+                snapshots.put(file.getAbsolutePath(), new FileHashSnapshot(hasher.hash(file)));
+            } else if (file.isDirectory()) {
+                snapshots.put(file.getAbsolutePath(), new DirSnapshot());
+            } else {
+                snapshots.put(file.getAbsolutePath(), new MissingFileSnapshot());
+            }
+        }
+        return new FileCollectionSnapshotImpl(snapshots);
+    }
+
+    private interface FileSnapshot extends Serializable {
+        boolean isUpToDate(FileSnapshot snapshot);
+    }
+
+    private static class FileHashSnapshot implements FileSnapshot {
+        private final byte[] hash;
+
+        public FileHashSnapshot(byte[] hash) {
+            this.hash = hash;
+        }
+
+        public boolean isUpToDate(FileSnapshot snapshot) {
+            if (!(snapshot instanceof FileHashSnapshot)) {
+                return false;
+            }
+
+            FileHashSnapshot other = (FileHashSnapshot) snapshot;
+            return Arrays.equals(hash, other.hash);
+        }
+
+        @Override
+        public String toString() {
+            return new BigInteger(1, hash).toString(16);
+        }
+    }
+
+    private static class DirSnapshot implements FileSnapshot {
+        public boolean isUpToDate(FileSnapshot snapshot) {
+            return snapshot instanceof DirSnapshot;
+        }
+    }
+
+    private static class MissingFileSnapshot implements FileSnapshot {
+        public boolean isUpToDate(FileSnapshot snapshot) {
+            return snapshot instanceof MissingFileSnapshot;
+        }
+    }
+
+    private static class FileCollectionSnapshotImpl implements FileCollectionSnapshot {
+        private final Map<String, FileSnapshot> snapshots;
+
+        public FileCollectionSnapshotImpl(Map<String, FileSnapshot> snapshots) {
+            this.snapshots = snapshots;
+        }
+
+        public FileCollection getFiles() {
+            List<File> files = new ArrayList<File>();
+            for (Map.Entry<String, FileSnapshot> entry : snapshots.entrySet()) {
+                if (entry.getValue() instanceof FileHashSnapshot) {
+                    files.add(new File(entry.getKey()));
+                }
+            }
+            return new SimpleFileCollection(files);
+        }
+
+        public void changesSince(FileCollectionSnapshot oldSnapshot, final ChangeListener<File> listener) {
+            FileCollectionSnapshotImpl other = (FileCollectionSnapshotImpl) oldSnapshot;
+            diff(snapshots, other.snapshots, new ChangeListener<Map.Entry<String, FileSnapshot>>() {
+                public void added(Map.Entry<String, FileSnapshot> element) {
+                    listener.added(new File(element.getKey()));
+                }
+
+                public void removed(Map.Entry<String, FileSnapshot> element) {
+                    listener.removed(new File(element.getKey()));
+                }
+
+                public void changed(Map.Entry<String, FileSnapshot> element) {
+                    listener.changed(new File(element.getKey()));
+                }
+            });
+        }
+
+        private void diff(Map<String, FileSnapshot> snapshots, Map<String, FileSnapshot> oldSnapshots,
+                          ChangeListener<Map.Entry<String, FileSnapshot>> listener) {
+            Map<String, FileSnapshot> otherSnapshots = new HashMap<String, FileSnapshot>(oldSnapshots);
+            for (Map.Entry<String, FileSnapshot> entry : snapshots.entrySet()) {
+                FileSnapshot otherFile = otherSnapshots.remove(entry.getKey());
+                if (otherFile == null) {
+                    listener.added(entry);
+                } else if (!entry.getValue().isUpToDate(otherFile)) {
+                    listener.changed(entry);
+                }
+            }
+            for (Map.Entry<String, FileSnapshot> entry : otherSnapshots.entrySet()) {
+                listener.removed(entry);
+            }
+        }
+
+        public Diff changesSince(final FileCollectionSnapshot oldSnapshot) {
+            final FileCollectionSnapshotImpl other = (FileCollectionSnapshotImpl) oldSnapshot;
+            return new Diff() {
+                public FileCollectionSnapshot applyTo(FileCollectionSnapshot snapshot) {
+                    return applyTo(snapshot, new NoOpChangeListener<Merge>());
+                }
+
+                public FileCollectionSnapshot applyTo(FileCollectionSnapshot snapshot, final ChangeListener<Merge> listener) {
+                    FileCollectionSnapshotImpl target = (FileCollectionSnapshotImpl) snapshot;
+                    final Map<String, FileSnapshot> newSnapshots = new HashMap<String, FileSnapshot>(target.snapshots);
+                    diff(snapshots, other.snapshots, new MapMergeChangeListener<String, FileSnapshot>(listener, newSnapshots));
+                    return new FileCollectionSnapshotImpl(newSnapshots);
+                }
+            };
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/DefaultTaskArtifactStateRepository.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/DefaultTaskArtifactStateRepository.java
index a50cf8b..baf5426 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/DefaultTaskArtifactStateRepository.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/DefaultTaskArtifactStateRepository.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2010 the original author or authors.
+ * Copyright 2011 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,59 +18,39 @@ package org.gradle.api.internal.changedetection;
 
 import org.apache.commons.lang.StringUtils;
 import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.TaskExecutionHistory;
 import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.internal.file.SimpleFileCollection;
+import org.gradle.api.internal.file.collections.SimpleFileCollection;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
 import org.gradle.cache.CacheRepository;
-import org.gradle.cache.DefaultSerializer;
-import org.gradle.cache.PersistentIndexedCache;
-import org.gradle.util.ChangeListener;
-import org.gradle.util.DiffUtil;
 
-import java.io.File;
-import java.io.Serializable;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Formatter;
+import java.util.List;
 
-import static java.util.Collections.*;
+import static java.util.Collections.singletonList;
 
 public class DefaultTaskArtifactStateRepository implements TaskArtifactStateRepository {
     private static final int MAX_OUT_OF_DATE_MESSAGES = 10;
     private static final Logger LOGGER = Logging.getLogger(DefaultTaskArtifactStateRepository.class);
-    private final CacheRepository repository;
-    private final FileSnapshotter inputFilesSnapshotter;
-    private final FileSnapshotter outputFilesSnapshotter;
-    private PersistentIndexedCache<String, TaskHistory> taskHistoryCache;
-    private DefaultSerializer<TaskHistory> serializer;
+    private final TaskHistoryRepository taskHistoryRepository;
+    private final UpToDateRule upToDateRule;
 
     public DefaultTaskArtifactStateRepository(CacheRepository repository, FileSnapshotter inputFilesSnapshotter, FileSnapshotter outputFilesSnapshotter) {
-        this.repository = repository;
-        this.inputFilesSnapshotter = inputFilesSnapshotter;
-        this.outputFilesSnapshotter = outputFilesSnapshotter;
+        this.taskHistoryRepository = new CacheBackedTaskHistoryRepository(repository, new CacheBackedFileSnapshotRepository(repository));
+        upToDateRule = new CompositeUpToDateRule(
+                new TaskTypeChangedUpToDateRule(),
+                new InputPropertiesChangedUpToDateRule(),
+                new OutputFilesChangedUpToDateRule(outputFilesSnapshotter),
+                new InputFilesChangedUpToDateRule(inputFilesSnapshotter));
     }
 
     public TaskArtifactState getStateFor(final TaskInternal task) {
-        if (taskHistoryCache == null) {
-            loadTasks(task);
-        }
-
-        return new TaskArtifactStateImpl(task);
-    }
-
-    private void loadTasks(TaskInternal task) {
-        serializer = new DefaultSerializer<TaskHistory>();
-        taskHistoryCache = repository.cache("taskArtifacts").forObject(task.getProject().getGradle()).open().openIndexedCache(serializer);
-    }
-
-    private static Set<String> outputFiles(TaskInternal task) {
-        Set<String> outputFiles = new HashSet<String>();
-        for (File file : task.getOutputs().getFiles()) {
-            outputFiles.add(file.getAbsolutePath());
-        }
-        return outputFiles;
+        return new TaskArtifactStateImpl(task, taskHistoryRepository.getHistory(task));
     }
 
-    private interface TaskExecution {
+    private interface TaskExecutionState {
         List<String> isUpToDate();
 
         boolean snapshot();
@@ -78,20 +58,7 @@ public class DefaultTaskArtifactStateRepository implements TaskArtifactStateRepo
         FileCollection getPreviousOutputFiles();
     }
 
-    private static class TaskHistory implements Serializable {
-        private static final int MAX_HISTORY_ENTRIES = 3;
-        private final List<TaskConfiguration> configurations = new ArrayList<TaskConfiguration>();
-
-        public void addConfiguration(TaskConfiguration configuration) {
-            configurations.add(0, configuration);
-            // Only keep a few of the most recent configurations
-            while (configurations.size() > MAX_HISTORY_ENTRIES) {
-                configurations.remove(MAX_HISTORY_ENTRIES);
-            }
-        }
-    }
-
-    private static class NoDeclaredArtifactsExecution implements TaskExecution {
+    private static class NoDeclaredArtifactsExecution implements TaskExecutionState {
         private final TaskInternal task;
 
         private NoDeclaredArtifactsExecution(TaskInternal task) {
@@ -115,38 +82,32 @@ public class DefaultTaskArtifactStateRepository implements TaskArtifactStateRepo
         }
     }
 
-    private static class HistoricExecution implements TaskExecution {
-        private final TaskHistory history;
+    private static class HistoricExecution implements TaskExecutionState {
         private final TaskInternal task;
-        private final TaskConfiguration lastExecution;
-        private final FileSnapshotter inputFilesSnapshotter;
-        private final FileSnapshotter outputFilesSnapshotter;
+        private final TaskExecution lastExecution;
         private boolean upToDate;
-        private TaskConfiguration thisExecution;
-        private FileCollectionSnapshot outputFilesBefore;
+        private final UpToDateRule rule;
+        private TaskExecution thisExecution;
+        private UpToDateRule.TaskUpToDateState upToDateState;
 
-        public HistoricExecution(TaskHistory history, TaskInternal task, TaskConfiguration lastExecution,
-                                 FileSnapshotter inputFilesSnapshotter, FileSnapshotter outputFilesSnapshotter) {
-            this.history = history;
+        public HistoricExecution(TaskInternal task, TaskHistoryRepository.History history, UpToDateRule rule) {
             this.task = task;
-            this.lastExecution = lastExecution;
-            this.inputFilesSnapshotter = inputFilesSnapshotter;
-            this.outputFilesSnapshotter = outputFilesSnapshotter;
+            this.lastExecution = history.getPreviousExecution();
+            this.thisExecution = history.getCurrentExecution();
+            this.rule = rule;
         }
 
         private void calcCurrentState() {
-            if (thisExecution != null) {
+            if (upToDateState != null) {
                 return;
             }
 
-            // Calculate current state - note this is potentially expensive
-            FileCollectionSnapshot inputFilesSnapshot = inputFilesSnapshotter.snapshot(task.getInputs().getFiles());
-            thisExecution = new TaskConfiguration(task, inputFilesSnapshot);
-            outputFilesBefore = outputFilesSnapshotter.snapshot(task.getOutputs().getFiles());
+            // Calculate initial state - note this is potentially expensive
+            upToDateState = rule.create(task, lastExecution, thisExecution);
         }
 
         public FileCollection getPreviousOutputFiles() {
-            return lastExecution != null ? lastExecution.outputFilesSnapshot.getFiles() : new SimpleFileCollection();
+            return lastExecution != null && lastExecution.getOutputFilesSnapshot() != null ? lastExecution.getOutputFilesSnapshot().getFiles() : new SimpleFileCollection();
         }
 
         public List<String> isUpToDate() {
@@ -157,77 +118,13 @@ public class DefaultTaskArtifactStateRepository implements TaskArtifactStateRepo
                 return singletonList(String.format("No history is available for %s.", task));
             }
 
-            if (!task.getClass().getName().equals(lastExecution.taskClass)) {
-                return singletonList(String.format("%s has changed type from '%s' to '%s'.", StringUtils.capitalize(
-                        task.toString()), lastExecution.taskClass, task.getClass().getName()));
-            }
-
             List<String> messages = new ArrayList<String>();
-            checkInputProperties(messages);
-            if (!messages.isEmpty()) {
-                return messages;
-            }
-
-            checkInputs(messages);
-            if (!messages.isEmpty()) {
-                return messages;
-            }
+            upToDateState.checkUpToDate(messages);
 
-            checkOutputs(messages);
-            if (!messages.isEmpty()) {
-                return messages;
+            if (messages.isEmpty()) {
+                upToDate = true;
             }
-
-            upToDate = true;
-            return emptyList();
-        }
-
-        private void checkOutputs(final Collection<String> messages) {
-            outputFilesBefore.changesSince(lastExecution.outputFilesSnapshot, new ChangeListener<File>() {
-                public void added(File element) {
-                    messages.add(String.format("Output file '%s' has been added for %s.", element, task));
-                }
-
-                public void removed(File element) {
-                    messages.add(String.format("Output file %s has been removed for %s.", element.getAbsolutePath(), task));
-                }
-
-                public void changed(File element) {
-                    messages.add(String.format("Output file %s for %s has changed.", element.getAbsolutePath(), task));
-                }
-            });
-        }
-
-        private void checkInputs(final Collection<String> messages) {
-            thisExecution.inputFilesSnapshot.changesSince(lastExecution.inputFilesSnapshot, new ChangeListener<File>() {
-                public void added(File file) {
-                    messages.add(String.format("Input file %s for %s added.", file, task));
-                }
-
-                public void removed(File file) {
-                    messages.add(String.format("Input file %s for %s removed.", file, task));
-                }
-
-                public void changed(File file) {
-                    messages.add(String.format("Input file %s for %s has changed.", file, task));
-                }
-            });
-        }
-
-        private void checkInputProperties(final Collection<String> messages) {
-            DiffUtil.diff(thisExecution.inputProperties, lastExecution.inputProperties, new ChangeListener<Map.Entry<String, Object>>() {
-                public void added(Map.Entry<String, Object> element) {
-                    messages.add(String.format("Input property '%s' has been added for %s", element.getKey(), task));
-                }
-
-                public void removed(Map.Entry<String, Object> element) {
-                    messages.add(String.format("Input property '%s' has been removed for %s", element.getKey(), task));
-                }
-
-                public void changed(Map.Entry<String, Object> element) {
-                    messages.add(String.format("Value of input property '%s' has changed for %s", element.getKey(), task));
-                }
-            });
+            return messages;
         }
 
         public boolean snapshot() {
@@ -237,54 +134,19 @@ public class DefaultTaskArtifactStateRepository implements TaskArtifactStateRepo
                 return false;
             }
 
-            FileCollectionSnapshot lastExecutionOutputFiles = lastExecution == null ? outputFilesSnapshotter.snapshot()
-                    : lastExecution.outputFilesSnapshot;
-            FileCollectionSnapshot newOutputFiles = outputFilesBefore.changesSince(lastExecutionOutputFiles).applyTo(
-                    lastExecutionOutputFiles, new ChangeListener<FileCollectionSnapshot.Merge>() {
-                        public void added(FileCollectionSnapshot.Merge element) {
-                            // Ignore added files
-                            element.ignore();
-                        }
-
-                        public void removed(FileCollectionSnapshot.Merge element) {
-                            // Discard any files removed since the task was last executed
-                        }
-
-                        public void changed(FileCollectionSnapshot.Merge element) {
-                            // Update any files which were change since the task was last executed
-                        }
-                    });
-            FileCollectionSnapshot outputFilesAfter = outputFilesSnapshotter.snapshot(task.getOutputs().getFiles());
-            thisExecution.outputFilesSnapshot = outputFilesAfter.changesSince(outputFilesBefore).applyTo(
-                    newOutputFiles);
-            history.addConfiguration(thisExecution);
+            upToDateState.snapshotAfterTask();
             return true;
         }
     }
 
-    private static class TaskConfiguration implements Serializable {
-        private final String taskClass;
-        private Set<String> outputFiles;
-        private Map<String, Object> inputProperties;
-        private FileCollectionSnapshot inputFilesSnapshot;
-        private FileCollectionSnapshot outputFilesSnapshot;
-
-        private TaskConfiguration(TaskInternal task, FileCollectionSnapshot inputFilesSnapshot) {
-            this.taskClass = task.getClass().getName();
-            this.outputFiles = outputFiles(task);
-            this.inputProperties = new HashMap<String, Object>(task.getInputs().getProperties());
-            this.inputFilesSnapshot = inputFilesSnapshot;
-        }
-    }
-
-    private class TaskArtifactStateImpl implements TaskArtifactState {
+    private class TaskArtifactStateImpl implements TaskArtifactState, TaskExecutionHistory {
         private final TaskInternal task;
-        private final TaskHistory history;
-        private final TaskExecution execution;
+        private final TaskHistoryRepository.History history;
+        private final TaskExecutionState execution;
 
-        public TaskArtifactStateImpl(TaskInternal task) {
+        public TaskArtifactStateImpl(TaskInternal task, TaskHistoryRepository.History history) {
             this.task = task;
-            history = getHistory();
+            this.history = history;
             execution = getExecution();
         }
 
@@ -313,52 +175,27 @@ public class DefaultTaskArtifactStateRepository implements TaskArtifactStateRepo
             return execution.getPreviousOutputFiles();
         }
 
-        private TaskHistory getHistory() {
-            ClassLoader original = serializer.getClassLoader();
-            serializer.setClassLoader(task.getClass().getClassLoader());
-            try {
-                TaskHistory history = taskHistoryCache.get(task.getPath());
-                return history == null ? new TaskHistory() : history;
-            } finally {
-                serializer.setClassLoader(original);
-            }
+        public TaskExecutionHistory getExecutionHistory() {
+            return this;
         }
 
-        public TaskExecution getExecution() {
+        public TaskExecutionState getExecution() {
             if (!task.getOutputs().getHasOutput()) {
                 return new NoDeclaredArtifactsExecution(task);
             }
-            Set<String> outputFiles = outputFiles(task);
-            TaskConfiguration bestMatch = null;
-            int bestMatchOverlap = 0;
-            for (TaskConfiguration configuration : history.configurations) {
-                if (outputFiles.size() == 0) {
-                    if (configuration.outputFiles.size() == 0) {
-                        bestMatch = configuration;
-                        break;
-                    }
-                }
-
-                Set<String> intersection = new HashSet<String>(outputFiles);
-                intersection.retainAll(configuration.outputFiles);
-                if (intersection.size() > bestMatchOverlap) {
-                    bestMatch = configuration;
-                    bestMatchOverlap = intersection.size();
-                }
-                if (bestMatchOverlap == outputFiles.size()) {
-                    break;
-                }
-            }
-            if (bestMatch == null) {
-                return new HistoricExecution(history, task, null, inputFilesSnapshotter, outputFilesSnapshotter);
-            }
-            return new HistoricExecution(history, task, bestMatch, inputFilesSnapshotter, outputFilesSnapshotter);
+            return new HistoricExecution(task, history, upToDateRule);
         }
 
-        public void update() {
+        public void afterTask() {
             if (execution.snapshot()) {
-                taskHistoryCache.put(task.getPath(), history);
+                history.update();
             }
         }
+
+        public void beforeTask() {
+        }
+
+        public void finished() {
+        }
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/FileCacheBroadcastTaskArtifactStateRepository.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/FileCacheBroadcastTaskArtifactStateRepository.java
new file mode 100644
index 0000000..c23ccad
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/FileCacheBroadcastTaskArtifactStateRepository.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection;
+
+import org.gradle.api.internal.TaskExecutionHistory;
+import org.gradle.api.internal.TaskInternal;
+
+public class FileCacheBroadcastTaskArtifactStateRepository implements TaskArtifactStateRepository {
+    private final TaskArtifactStateRepository repository;
+    private final FileCacheListener listener;
+
+    public FileCacheBroadcastTaskArtifactStateRepository(TaskArtifactStateRepository repository, FileCacheListener listener) {
+        this.repository = repository;
+        this.listener = listener;
+    }
+
+    public TaskArtifactState getStateFor(final TaskInternal task) {
+        final TaskArtifactState state = repository.getStateFor(task);
+        return new TaskArtifactState() {
+            public boolean isUpToDate() {
+                listener.cacheable(task.getInputs().getFiles());
+                listener.cacheable(task.getOutputs().getFiles());
+
+                return state.isUpToDate();
+            }
+
+            public void beforeTask() {
+                if (task.getOutputs().getHasOutput()) {
+                    listener.invalidate(task.getOutputs().getFiles());
+                } else {
+                    listener.invalidateAll();
+                }
+                state.beforeTask();
+            }
+
+            public void afterTask() {
+                listener.cacheable(task.getOutputs().getFiles());
+                state.afterTask();
+            }
+
+            public void finished() {
+                state.finished();
+            }
+
+            public TaskExecutionHistory getExecutionHistory() {
+                return state.getExecutionHistory();
+            }
+        };
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/FileCacheListener.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/FileCacheListener.java
new file mode 100644
index 0000000..1906d55
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/FileCacheListener.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection;
+
+import org.gradle.api.file.FileCollection;
+
+public interface FileCacheListener {
+    /**
+     * Indicates that the given files are cacheable, and are not expected to change until invalidated.
+     */
+    void cacheable(FileCollection files);
+
+    /**
+     * Indicates that the given files may have changed, and should no longer be considered cacheable.
+     */
+    void invalidate(FileCollection files);
+
+    /**
+     * Indicates that any file may have changed, and should no longer be considered cacheable.
+     */
+    void invalidateAll();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/FileSnapshotRepository.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/FileSnapshotRepository.java
new file mode 100644
index 0000000..b83e755
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/FileSnapshotRepository.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection;
+
+public interface FileSnapshotRepository {
+    FileCollectionSnapshot get(Long id);
+
+    Long add(FileCollectionSnapshot snapshot);
+
+    void remove(Long id);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/FileSnapshotter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/FileSnapshotter.java
index 3a3eb3b..fa65cfc 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/FileSnapshotter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/FileSnapshotter.java
@@ -1,35 +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.api.internal.changedetection;
-
-import org.gradle.api.file.FileCollection;
-
-public interface FileSnapshotter {
-    /**
-     * Creates an empty snapshot, which changes can be later merged into.
-     *
-     * @return The snapshot.
-     */
-    FileCollectionSnapshot snapshot();
-
-    /**
-     * Creates a snapshot of the contents of the given collection
-     *
-     * @param files The files to snapshot
-     * @return The snapshot.
-     */
-    FileCollectionSnapshot snapshot(FileCollection files);
-}
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection;
+
+import org.gradle.api.file.FileCollection;
+
+public interface FileSnapshotter {
+    /**
+     * Creates an empty snapshot, which changes can be later merged into.
+     *
+     * @return The snapshot.
+     */
+    FileCollectionSnapshot emptySnapshot();
+
+    /**
+     * Creates a snapshot of the contents of the given collection
+     *
+     * @param files The files to snapshot
+     * @return The snapshot.
+     */
+    FileCollectionSnapshot snapshot(FileCollection files);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/InMemoryIndexedCache.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/InMemoryIndexedCache.java
new file mode 100644
index 0000000..a69e4eb
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/InMemoryIndexedCache.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection;
+
+import org.gradle.api.UncheckedIOException;
+import org.gradle.cache.PersistentIndexedCache;
+import org.gradle.util.UncheckedException;
+
+import java.io.*;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A simple in-memory cache, used by the testing fixtures.
+ */
+public class InMemoryIndexedCache<K, V> implements PersistentIndexedCache<K, V> {
+    Map<Object, byte[]> entries = new HashMap<Object, byte[]>();
+
+    public V get(K key) {
+        byte[] serialised = entries.get(key);
+        if (serialised == null) {
+            return null;
+        }
+        try {
+            ByteArrayInputStream instr = new ByteArrayInputStream(serialised);
+            return (V)new ObjectInputStream(instr).readObject();
+        } catch (Exception e) {
+            throw UncheckedException.asUncheckedException(e);
+        }
+    }
+
+    public void put(K key, V value) {
+        ByteArrayOutputStream outstr = new ByteArrayOutputStream();
+        try {
+            ObjectOutputStream objstr = new ObjectOutputStream(outstr);
+            objstr.writeObject(value);
+            objstr.close();
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+
+        entries.put(key, outstr.toByteArray());
+    }
+
+    public void remove(K key) {
+        entries.remove(key);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/InputFilesChangedUpToDateRule.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/InputFilesChangedUpToDateRule.java
new file mode 100644
index 0000000..7df4355
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/InputFilesChangedUpToDateRule.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection;
+
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.util.ChangeListener;
+
+import java.io.File;
+import java.util.Collection;
+
+/**
+ * A rule which marks a task out-of-date when its input files change.
+ */
+public class InputFilesChangedUpToDateRule implements UpToDateRule {
+    private final FileSnapshotter inputFilesSnapshotter;
+
+    public InputFilesChangedUpToDateRule(FileSnapshotter inputFilesSnapshotter) {
+        this.inputFilesSnapshotter = inputFilesSnapshotter;
+    }
+
+    public TaskUpToDateState create(final TaskInternal task, final TaskExecution previousExecution, final TaskExecution currentExecution) {
+        final FileCollectionSnapshot inputFilesSnapshot = inputFilesSnapshotter.snapshot(task.getInputs().getFiles());
+
+        return new TaskUpToDateState() {
+            public void checkUpToDate(final Collection<String> messages) {
+                if (previousExecution.getInputFilesSnapshot() == null) {
+                    messages.add(String.format("Input file history is not available for %s.", task));
+                    return;
+                }
+                inputFilesSnapshot.changesSince(previousExecution.getInputFilesSnapshot(), new ChangeListener<File>() {
+                    public void added(File file) {
+                        messages.add(String.format("Input file %s for %s added.", file, task));
+                    }
+
+                    public void removed(File file) {
+                        messages.add(String.format("Input file %s for %s removed.", file, task));
+                    }
+
+                    public void changed(File file) {
+                        messages.add(String.format("Input file %s for %s has changed.", file, task));
+                    }
+                });
+            }
+
+            public void snapshotAfterTask() {
+                currentExecution.setInputFilesSnapshot(inputFilesSnapshot);
+            }
+        };
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/InputPropertiesChangedUpToDateRule.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/InputPropertiesChangedUpToDateRule.java
new file mode 100644
index 0000000..cbff546
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/InputPropertiesChangedUpToDateRule.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection;
+
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.util.ChangeListener;
+import org.gradle.util.DiffUtil;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A rule which marks a task out-of-date when its input properties change.
+ */
+public class InputPropertiesChangedUpToDateRule implements UpToDateRule {
+    public TaskUpToDateState create(final TaskInternal task, final TaskExecution previousExecution, final TaskExecution currentExecution) {
+        final Map<String, Object> properties = new HashMap<String, Object>(task.getInputs().getProperties());
+        currentExecution.setInputProperties(properties);
+
+        return new TaskUpToDateState() {
+            public void checkUpToDate(final Collection<String> messages) {
+                DiffUtil.diff(properties, previousExecution.getInputProperties(), new ChangeListener<Map.Entry<String, Object>>() {
+                    public void added(Map.Entry<String, Object> element) {
+                        messages.add(String.format("Input property '%s' has been added for %s", element.getKey(), task));
+                    }
+
+                    public void removed(Map.Entry<String, Object> element) {
+                        messages.add(String.format("Input property '%s' has been removed for %s", element.getKey(), task));
+                    }
+
+                    public void changed(Map.Entry<String, Object> element) {
+                        messages.add(String.format("Value of input property '%s' has changed for %s", element.getKey(), task));
+                    }
+                });
+            }
+
+            public void snapshotAfterTask() {
+            }
+        };
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/OutputFilesChangedUpToDateRule.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/OutputFilesChangedUpToDateRule.java
new file mode 100644
index 0000000..57eca84
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/OutputFilesChangedUpToDateRule.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection;
+
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.util.ChangeListener;
+
+import java.io.File;
+import java.util.Collection;
+
+/**
+ * A rule which marks a task out-of-date when its output files change.
+ */
+public class OutputFilesChangedUpToDateRule implements UpToDateRule {
+    private final FileSnapshotter outputFilesSnapshotter;
+
+    public OutputFilesChangedUpToDateRule(FileSnapshotter outputFilesSnapshotter) {
+        this.outputFilesSnapshotter = outputFilesSnapshotter;
+    }
+
+    public TaskUpToDateState create(final TaskInternal task, final TaskExecution previousExecution, final TaskExecution currentExecution) {
+        final FileCollectionSnapshot outputFilesBefore = outputFilesSnapshotter.snapshot(task.getOutputs().getFiles());
+
+        return new TaskUpToDateState() {
+            public void checkUpToDate(final Collection<String> messages) {
+                if (previousExecution.getOutputFilesSnapshot() == null) {
+                    messages.add(String.format("Output file history is not available for %s.", task));
+                    return;
+                }
+                outputFilesBefore.changesSince(previousExecution.getOutputFilesSnapshot(), new ChangeListener<File>() {
+                    public void added(File element) {
+                        messages.add(String.format("Output file '%s' has been added for %s.", element, task));
+                    }
+
+                    public void removed(File element) {
+                        messages.add(String.format("Output file %s has been removed for %s.", element.getAbsolutePath(), task));
+                    }
+
+                    public void changed(File element) {
+                        messages.add(String.format("Output file %s for %s has changed.", element.getAbsolutePath(), task));
+                    }
+                });
+            }
+
+            public void snapshotAfterTask() {
+                FileCollectionSnapshot lastExecutionOutputFiles;
+                if (previousExecution == null || previousExecution.getOutputFilesSnapshot() == null) {
+                    lastExecutionOutputFiles = outputFilesSnapshotter.emptySnapshot();
+                } else {
+                    lastExecutionOutputFiles = previousExecution.getOutputFilesSnapshot();
+                }
+                FileCollectionSnapshot newOutputFiles = outputFilesBefore.changesSince(lastExecutionOutputFiles).applyTo(
+                        lastExecutionOutputFiles, new ChangeListener<FileCollectionSnapshot.Merge>() {
+                            public void added(FileCollectionSnapshot.Merge element) {
+                                // Ignore added files
+                                element.ignore();
+                            }
+
+                            public void removed(FileCollectionSnapshot.Merge element) {
+                                // Discard any files removed since the task was last executed
+                            }
+
+                            public void changed(FileCollectionSnapshot.Merge element) {
+                                // Update any files which were change since the task was last executed
+                            }
+                        });
+                FileCollectionSnapshot outputFilesAfter = outputFilesSnapshotter.snapshot(task.getOutputs().getFiles());
+                currentExecution.setOutputFilesSnapshot(outputFilesAfter.changesSince(outputFilesBefore).applyTo(newOutputFiles));
+            }
+        };
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/OutputFilesSnapshotter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/OutputFilesSnapshotter.java
index d9bcca1..e783f2c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/OutputFilesSnapshotter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/OutputFilesSnapshotter.java
@@ -51,8 +51,8 @@ public class OutputFilesSnapshotter implements FileSnapshotter {
         dirIdentiferCache = cacheRepository.cache("outputFileStates").open().openIndexedCache();
     }
 
-    public FileCollectionSnapshot snapshot() {
-        return new OutputFilesSnapshot(new HashMap<String, Long>(), snapshotter.snapshot());
+    public FileCollectionSnapshot emptySnapshot() {
+        return new OutputFilesSnapshot(new HashMap<String, Long>(), snapshotter.emptySnapshot());
     }
 
     public FileCollectionSnapshot snapshot(FileCollection files) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/ShortCircuitTaskArtifactStateRepository.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/ShortCircuitTaskArtifactStateRepository.java
index 7bd7639..1bd9061 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/ShortCircuitTaskArtifactStateRepository.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/ShortCircuitTaskArtifactStateRepository.java
@@ -1,47 +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.api.internal.changedetection;
-
-import org.gradle.StartParameter;
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.TaskInternal;
-
-public class ShortCircuitTaskArtifactStateRepository implements TaskArtifactStateRepository {
-    private final StartParameter startParameter;
-    private final TaskArtifactStateRepository repository;
-
-    public ShortCircuitTaskArtifactStateRepository(StartParameter startParameter, TaskArtifactStateRepository repository) {
-        this.startParameter = startParameter;
-        this.repository = repository;
-    }
-
-    public TaskArtifactState getStateFor(final TaskInternal task) {
-        final TaskArtifactState state = repository.getStateFor(task);
-        return new TaskArtifactState() {
-            public boolean isUpToDate() {
-                return !startParameter.isNoOpt() && task.getOutputs().getUpToDateSpec().isSatisfiedBy(task) && state.isUpToDate();
-            }
-
-            public FileCollection getOutputFiles() {
-                return state.getOutputFiles();
-            }
-
-            public void update() {
-                state.update();
-            }
-        };
-    }
-}
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection;
+
+import org.gradle.StartParameter;
+import org.gradle.api.internal.TaskExecutionHistory;
+import org.gradle.api.internal.TaskInternal;
+
+public class ShortCircuitTaskArtifactStateRepository implements TaskArtifactStateRepository {
+    private final StartParameter startParameter;
+    private final TaskArtifactStateRepository repository;
+
+    public ShortCircuitTaskArtifactStateRepository(StartParameter startParameter, TaskArtifactStateRepository repository) {
+        this.startParameter = startParameter;
+        this.repository = repository;
+    }
+
+    public TaskArtifactState getStateFor(final TaskInternal task) {
+        final TaskArtifactState state = repository.getStateFor(task);
+        return new TaskArtifactState() {
+            public boolean isUpToDate() {
+                return !startParameter.isNoOpt() && task.getOutputs().getUpToDateSpec().isSatisfiedBy(task) && state.isUpToDate();
+            }
+
+            public TaskExecutionHistory getExecutionHistory() {
+                return state.getExecutionHistory();
+            }
+
+            public void beforeTask() {
+                state.beforeTask();
+            }
+
+            public void afterTask() {
+                state.afterTask();
+            }
+
+            public void finished() {
+                state.finished();
+            }
+        };
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/TaskArtifactState.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/TaskArtifactState.java
index ec055a4..5fe4110 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/TaskArtifactState.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/TaskArtifactState.java
@@ -20,14 +20,29 @@ import org.gradle.api.internal.TaskExecutionHistory;
 /**
  * Encapsulates the state of the task when its outputs were last generated.
  */
-public interface TaskArtifactState extends TaskExecutionHistory {
+public interface TaskArtifactState {
     /**
      * Returns true if the task outputs were generated using the given task inputs.
      */
     boolean isUpToDate();
 
     /**
-     * Marks current state as valid.
+     * Called before the task is to be executed. Note that {@link #isUpToDate()} may not necessarily have been called.
      */
-    void update();
+    void beforeTask();
+
+    /**
+     * Called on successful completion of task execution.
+     */
+    void afterTask();
+
+    /**
+     * Called when this state is finished with.
+     */
+    void finished();
+
+    /**
+     * Returns the history for this task.
+     */
+    TaskExecutionHistory getExecutionHistory();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/TaskExecution.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/TaskExecution.java
new file mode 100644
index 0000000..16aff96
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/TaskExecution.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The persistent state for a single task execution.
+ */
+public abstract class TaskExecution implements Serializable {
+    private String taskClass;
+    private Map<String, Object> inputProperties;
+    private Set<String> outputFiles;
+
+    public Set<String> getOutputFiles() {
+        return outputFiles;
+    }
+
+    public void setOutputFiles(Set<String> outputFiles) {
+        this.outputFiles = outputFiles;
+    }
+
+    public String getTaskClass() {
+        return taskClass;
+    }
+
+    public void setTaskClass(String taskClass) {
+        this.taskClass = taskClass;
+    }
+
+    public Map<String, Object> getInputProperties() {
+        return inputProperties;
+    }
+
+    public void setInputProperties(Map<String, Object> inputProperties) {
+        this.inputProperties = inputProperties;
+    }
+
+    /**
+     * @return May return null.
+     */
+    public abstract FileCollectionSnapshot getOutputFilesSnapshot();
+
+    public abstract void setOutputFilesSnapshot(FileCollectionSnapshot outputFilesSnapshot);
+
+    /**
+     * @return May return null.
+     */
+    public abstract FileCollectionSnapshot getInputFilesSnapshot();
+
+    public abstract void setInputFilesSnapshot(FileCollectionSnapshot inputFilesSnapshot);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/TaskHistoryRepository.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/TaskHistoryRepository.java
new file mode 100644
index 0000000..9f71553
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/TaskHistoryRepository.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection;
+
+import org.gradle.api.internal.TaskInternal;
+
+public interface TaskHistoryRepository {
+    History getHistory(TaskInternal task);
+
+    interface History {
+        TaskExecution getPreviousExecution();
+
+        TaskExecution getCurrentExecution();
+
+        void update();
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/TaskTypeChangedUpToDateRule.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/TaskTypeChangedUpToDateRule.java
new file mode 100644
index 0000000..c166808
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/TaskTypeChangedUpToDateRule.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection;
+
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.internal.TaskInternal;
+
+import java.util.Collection;
+
+/**
+ * A rule which marks a task out-of-date when its implementation class changes.
+ */
+public class TaskTypeChangedUpToDateRule implements UpToDateRule {
+    public TaskUpToDateState create(final TaskInternal task, final TaskExecution previousExecution, final TaskExecution currentExecution) {
+        final String taskClass = task.getClass().getName();
+        currentExecution.setTaskClass(taskClass);
+
+        return new TaskUpToDateState() {
+            public void checkUpToDate(Collection<String> messages) {
+                if (!taskClass.equals(previousExecution.getTaskClass())) {
+                    messages.add(String.format("%s has changed type from '%s' to '%s'.", StringUtils.capitalize(task.toString()), previousExecution.getTaskClass(), task.getClass().getName()));
+                }
+            }
+
+            public void snapshotAfterTask() {
+            }
+        };
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/UpToDateRule.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/UpToDateRule.java
new file mode 100644
index 0000000..2f47511
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/UpToDateRule.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection;
+
+import org.gradle.api.internal.TaskInternal;
+
+import java.util.Collection;
+
+public interface UpToDateRule {
+    /**
+     * Creates the transient state for the given task.
+     *
+     * @param task The task to be executed.
+     * @param previousExecution The previous execution for this task, if any. May be null.
+     * @param currentExecution The current execution. The rule may mutate this.
+     * @return The state.
+     */
+    TaskUpToDateState create(TaskInternal task, TaskExecution previousExecution, TaskExecution currentExecution);
+
+    interface TaskUpToDateState {
+        /**
+         * Checks if the task is up-to-date. If so, this method must add at least 1 message explaining why the task is out-of-date to the given collection. Note that this method may not be called for
+         * a given execution. Also note, this method is called only when the previous execution is not null.
+         *
+         * @param messages The out-of-date messages.
+         */
+        void checkUpToDate(Collection<String> messages);
+
+        /**
+         * Snapshot any final state after the task has executed. This method is executed only if the task is to be executed. Any persistent state should be added to the {@link TaskExecution} object
+         * passed to {@link UpToDateRule#create}.
+         */
+        void snapshotAfterTask();
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AbstractFileCollection.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AbstractFileCollection.java
index f3f6ab6..c268672 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AbstractFileCollection.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AbstractFileCollection.java
@@ -19,17 +19,19 @@ import groovy.lang.Closure;
 import org.apache.commons.lang.StringUtils;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.file.FileTree;
+import org.gradle.api.internal.file.collections.*;
 import org.gradle.api.internal.tasks.DefaultTaskDependency;
 import org.gradle.api.specs.Spec;
 import org.gradle.api.specs.Specs;
 import org.gradle.api.tasks.StopExecutionException;
 import org.gradle.api.tasks.TaskDependency;
+import org.gradle.api.tasks.util.PatternSet;
 import org.gradle.util.GUtil;
 
 import java.io.File;
 import java.util.*;
 
-public abstract class AbstractFileCollection implements FileCollection {
+public abstract class AbstractFileCollection implements FileCollection, MinimalFileSet {
     /**
      * Returns the display name of this file collection. Used in log and error messages.
      *
@@ -108,9 +110,7 @@ public abstract class AbstractFileCollection implements FileCollection {
     }
 
     protected void addAsFileSet(Object builder, String nodeName) {
-        for (DefaultConfigurableFileTree fileTree : getAsFileTrees()) {
-            fileTree.addToAntBuilder(builder, nodeName, AntType.FileSet);
-        }
+        new AntFileSetBuilder(getAsFileTrees()).addToAntBuilder(builder, nodeName);
     }
 
     protected void addAsResourceCollection(Object builder, String nodeName) {
@@ -118,15 +118,15 @@ public abstract class AbstractFileCollection implements FileCollection {
     }
 
     /**
-     * Returns this collection as a set of {@link DefaultConfigurableFileTree} instances.
+     * Returns this collection as a set of {@link DirectoryFileTree} instances.
      */
-    protected Collection<DefaultConfigurableFileTree> getAsFileTrees() {
-        List<DefaultConfigurableFileTree> fileTrees = new ArrayList<DefaultConfigurableFileTree>();
+    protected Collection<DirectoryFileTree> getAsFileTrees() {
+        List<DirectoryFileTree> fileTrees = new ArrayList<DirectoryFileTree>();
         for (File file : getFiles()) {
             if (file.isFile()) {
-                DefaultConfigurableFileTree fileTree = new DefaultConfigurableFileTree(file.getParentFile(), null, null);
-                fileTree.include(new String[]{file.getName()});
-                fileTrees.add(fileTree);
+                PatternSet patternSet = new PatternSet();
+                patternSet.include(new String[]{file.getName()});
+                fileTrees.add(new DirectoryFileTree(file.getParentFile(), patternSet));
             }
         }
         return fileTrees;
@@ -178,16 +178,10 @@ public abstract class AbstractFileCollection implements FileCollection {
     public FileTree getAsFileTree() {
         return new CompositeFileTree() {
             @Override
-            public TaskDependency getBuildDependencies() {
-                return AbstractFileCollection.this.getBuildDependencies();
-            }
-
-            @Override
-            protected void addSourceCollections(Collection<FileCollection> sources) {
-                TaskDependency taskDependency = AbstractFileCollection.this.getBuildDependencies();
-                for (File file : AbstractFileCollection.this.getFiles()) {
-                    sources.add(new SingletonFileTree(file, taskDependency));
-                }
+            public void resolve(FileCollectionResolveContext context) {
+                ResolvableFileCollectionResolveContext nested = context.newContext();
+                nested.add(AbstractFileCollection.this);
+                context.add(nested.resolveAsFileTrees());
             }
 
             @Override
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AbstractFileResolver.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AbstractFileResolver.java
index 81cf729..27dd1ea 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AbstractFileResolver.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AbstractFileResolver.java
@@ -1,209 +1,210 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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 groovy.lang.Closure;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.PathValidation;
-import org.gradle.api.UncheckedIOException;
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.file.FileTree;
-import org.gradle.util.GFileUtils;
-import org.gradle.util.OperatingSystem;
-
-import java.io.File;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.util.concurrent.Callable;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public abstract class AbstractFileResolver implements FileResolver {
-    private static final Pattern URI_SCHEME = Pattern.compile("[a-zA-Z][a-zA-Z0-9+-\\.]*:.+");
-    private static final Pattern ENCODED_URI = Pattern.compile("%([0-9a-fA-F]{2})");
-
-    public FileResolver withBaseDir(Object path) {
-        return new BaseDirConverter(resolve(path));
-    }
-
-    public File resolve(Object path) {
-        return resolve(path, PathValidation.NONE);
-    }
-
-    public File resolve(Object path, PathValidation validation) {
-        File file = doResolve(path);
-        file = GFileUtils.canonicalise(file);
-        validate(file, validation);
-        return file;
-    }
-
-    public FileSource resolveLater(final Object path) {
-        return new FileSource() {
-            public File get() {
-                return resolve(path);
-            }
-        };
-    }
-
-    public URI resolveUri(Object path) {
-        return convertObjectToURI(path);
-    }
-
-    protected abstract File doResolve(Object path);
-
-    protected URI convertObjectToURI(Object path) {
-        Object object = unpack(path);
-        Object converted = convertToFileOrUri(object);
-        if (converted instanceof File) {
-            return resolve(converted).toURI();
-        }
-        return (URI) converted;
-    }
-
-    protected File convertObjectToFile(Object path) {
-        Object object = unpack(path);
-        if (object == null) {
-            return null;
-        }
-        Object converted = convertToFileOrUri(object);
-        if (converted instanceof File) {
-            return (File) converted;
-        }
-        throw new InvalidUserDataException(String.format("Cannot convert URL '%s' to a file.", converted));
-    }
-
-    private Object convertToFileOrUri(Object path) {
-        if (path instanceof File) {
-            return path;
-        }
-
-        if (path instanceof URL) {
-            try {
-                path = ((URL) path).toURI();
-            } catch (URISyntaxException e) {
-                throw new UncheckedIOException(e);
-            }
-        }
-
-        if (path instanceof URI) {
-            URI uri = (URI) path;
-            if (uri.getScheme().equals("file")) {
-                return new File(uri.getPath());
-            }
-            return uri;
-        }
-
-        String str = path.toString();
-        if (str.startsWith("file:")) {
-            return new File(uriDecode(str.substring(5)));
-        }
-
-        for (File file : File.listRoots()) {
-            String rootPath = file.getAbsolutePath();
-            String normalisedStr = str;
-            if (!OperatingSystem.current().isCaseSensitiveFileSystem()) {
-                rootPath = rootPath.toLowerCase();
-                normalisedStr = normalisedStr.toLowerCase();
-            }
-            if (normalisedStr.startsWith(rootPath) || normalisedStr.startsWith(rootPath.replace(File.separatorChar,
-                    '/'))) {
-                return new File(str);
-            }
-        }
-
-        // Check if string starts with a URI scheme
-        if (URI_SCHEME.matcher(str).matches()) {
-            try {
-                return new URI(str);
-            } catch (URISyntaxException e) {
-                throw new UncheckedIOException(e);
-            }
-        }
-
-        return new File(str);
-    }
-
-    private String uriDecode(String path) {
-        StringBuffer builder = new StringBuffer();
-        Matcher matcher = ENCODED_URI.matcher(path);
-        while (matcher.find()) {
-            String val = matcher.group(1);
-            matcher.appendReplacement(builder, String.valueOf((char) (Integer.parseInt(val, 16))));
-        }
-        matcher.appendTail(builder);
-        return builder.toString();
-    }
-
-    private Object unpack(Object path) {
-        Object current = path;
-        while (current != null) {
-            if (current instanceof Closure) {
-                current = ((Closure) current).call();
-            } else if (current instanceof Callable) {
-                try {
-                    current = ((Callable) current).call();
-                } catch (Exception e) {
-                    throw new RuntimeException(e);
-                }
-            } else if (current instanceof FileSource) {
-                return ((FileSource)current).get();
-            } else {
-                return current;
-            }
-        }
-        return null;
-    }
-
-    protected void validate(File file, PathValidation validation) {
-        switch (validation) {
-            case NONE:
-                break;
-            case EXISTS:
-                if (!file.exists()) {
-                    throw new InvalidUserDataException(String.format("File '%s' does not exist.", file));
-                }
-                break;
-            case FILE:
-                if (!file.exists()) {
-                    throw new InvalidUserDataException(String.format("File '%s' does not exist.", file));
-                }
-                if (!file.isFile()) {
-                    throw new InvalidUserDataException(String.format("File '%s' is not a file.", file));
-                }
-                break;
-            case DIRECTORY:
-                if (!file.exists()) {
-                    throw new InvalidUserDataException(String.format("Directory '%s' does not exist.", file));
-                }
-                if (!file.isDirectory()) {
-                    throw new InvalidUserDataException(String.format("Directory '%s' is not a directory.", file));
-                }
-                break;
-        }
-    }
-
-    public FileCollection resolveFiles(Object... paths) {
-        if (paths.length == 1 && paths[0] instanceof FileCollection) {
-            return (FileCollection) paths[0];
-        }
-        return new PathResolvingFileCollection(this, null, paths);
-    }
-
-    public FileTree resolveFilesAsTree(Object... paths) {
-        return resolveFiles(paths).getAsFileTree();
-    }
-}
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 groovy.lang.Closure;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.PathValidation;
+import org.gradle.api.UncheckedIOException;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.file.FileTree;
+import org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection;
+import org.gradle.util.GFileUtils;
+import org.gradle.util.OperatingSystem;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.concurrent.Callable;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public abstract class AbstractFileResolver implements FileResolver {
+    private static final Pattern URI_SCHEME = Pattern.compile("[a-zA-Z][a-zA-Z0-9+-\\.]*:.+");
+    private static final Pattern ENCODED_URI = Pattern.compile("%([0-9a-fA-F]{2})");
+
+    public FileResolver withBaseDir(Object path) {
+        return new BaseDirConverter(resolve(path));
+    }
+
+    public File resolve(Object path) {
+        return resolve(path, PathValidation.NONE);
+    }
+
+    public File resolve(Object path, PathValidation validation) {
+        File file = doResolve(path);
+        file = GFileUtils.canonicalise(file);
+        validate(file, validation);
+        return file;
+    }
+
+    public FileSource resolveLater(final Object path) {
+        return new FileSource() {
+            public File get() {
+                return resolve(path);
+            }
+        };
+    }
+
+    public URI resolveUri(Object path) {
+        return convertObjectToURI(path);
+    }
+
+    protected abstract File doResolve(Object path);
+
+    protected URI convertObjectToURI(Object path) {
+        Object object = unpack(path);
+        Object converted = convertToFileOrUri(object);
+        if (converted instanceof File) {
+            return resolve(converted).toURI();
+        }
+        return (URI) converted;
+    }
+
+    protected File convertObjectToFile(Object path) {
+        Object object = unpack(path);
+        if (object == null) {
+            return null;
+        }
+        Object converted = convertToFileOrUri(object);
+        if (converted instanceof File) {
+            return (File) converted;
+        }
+        throw new InvalidUserDataException(String.format("Cannot convert URL '%s' to a file.", converted));
+    }
+
+    private Object convertToFileOrUri(Object path) {
+        if (path instanceof File) {
+            return path;
+        }
+
+        if (path instanceof URL) {
+            try {
+                path = ((URL) path).toURI();
+            } catch (URISyntaxException e) {
+                throw new UncheckedIOException(e);
+            }
+        }
+
+        if (path instanceof URI) {
+            URI uri = (URI) path;
+            if (uri.getScheme().equals("file")) {
+                return new File(uri.getPath());
+            }
+            return uri;
+        }
+
+        String str = path.toString();
+        if (str.startsWith("file:")) {
+            return new File(uriDecode(str.substring(5)));
+        }
+
+        for (File file : File.listRoots()) {
+            String rootPath = file.getAbsolutePath();
+            String normalisedStr = str;
+            if (!OperatingSystem.current().isCaseSensitiveFileSystem()) {
+                rootPath = rootPath.toLowerCase();
+                normalisedStr = normalisedStr.toLowerCase();
+            }
+            if (normalisedStr.startsWith(rootPath) || normalisedStr.startsWith(rootPath.replace(File.separatorChar,
+                    '/'))) {
+                return new File(str);
+            }
+        }
+
+        // Check if string starts with a URI scheme
+        if (URI_SCHEME.matcher(str).matches()) {
+            try {
+                return new URI(str);
+            } catch (URISyntaxException e) {
+                throw new UncheckedIOException(e);
+            }
+        }
+
+        return new File(str);
+    }
+
+    private String uriDecode(String path) {
+        StringBuffer builder = new StringBuffer();
+        Matcher matcher = ENCODED_URI.matcher(path);
+        while (matcher.find()) {
+            String val = matcher.group(1);
+            matcher.appendReplacement(builder, String.valueOf((char) (Integer.parseInt(val, 16))));
+        }
+        matcher.appendTail(builder);
+        return builder.toString();
+    }
+
+    private Object unpack(Object path) {
+        Object current = path;
+        while (current != null) {
+            if (current instanceof Closure) {
+                current = ((Closure) current).call();
+            } else if (current instanceof Callable) {
+                try {
+                    current = ((Callable) current).call();
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            } else if (current instanceof FileSource) {
+                return ((FileSource)current).get();
+            } else {
+                return current;
+            }
+        }
+        return null;
+    }
+
+    protected void validate(File file, PathValidation validation) {
+        switch (validation) {
+            case NONE:
+                break;
+            case EXISTS:
+                if (!file.exists()) {
+                    throw new InvalidUserDataException(String.format("File '%s' does not exist.", file));
+                }
+                break;
+            case FILE:
+                if (!file.exists()) {
+                    throw new InvalidUserDataException(String.format("File '%s' does not exist.", file));
+                }
+                if (!file.isFile()) {
+                    throw new InvalidUserDataException(String.format("File '%s' is not a file.", file));
+                }
+                break;
+            case DIRECTORY:
+                if (!file.exists()) {
+                    throw new InvalidUserDataException(String.format("Directory '%s' does not exist.", file));
+                }
+                if (!file.isDirectory()) {
+                    throw new InvalidUserDataException(String.format("Directory '%s' is not a directory.", file));
+                }
+                break;
+        }
+    }
+
+    public FileCollection resolveFiles(Object... paths) {
+        if (paths.length == 1 && paths[0] instanceof FileCollection) {
+            return (FileCollection) paths[0];
+        }
+        return new DefaultConfigurableFileCollection(this, null, paths);
+    }
+
+    public FileTree resolveFilesAsTree(Object... paths) {
+        return resolveFiles(paths).getAsFileTree();
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AbstractFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AbstractFileTree.java
index 1e49cd8..8c04bf7 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AbstractFileTree.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AbstractFileTree.java
@@ -19,6 +19,7 @@ import groovy.lang.Closure;
 import org.codehaus.groovy.runtime.DefaultGroovyMethods;
 import org.gradle.api.file.*;
 import org.gradle.api.specs.Spec;
+import org.gradle.api.tasks.TaskDependency;
 import org.gradle.api.tasks.util.PatternFilterable;
 import org.gradle.api.tasks.util.PatternSet;
 import org.gradle.util.ConfigureUtil;
@@ -83,16 +84,20 @@ public abstract class AbstractFileTree extends AbstractFileCollection implements
     /**
      * Visits all the files of this tree.
      */
-    protected void visitAll() {
+    protected boolean visitAll() {
+        final AtomicBoolean hasContent = new AtomicBoolean();
         visit(new FileVisitor() {
             public void visitDir(FileVisitDetails dirDetails) {
                 dirDetails.getFile();
+                hasContent.set(true);
             }
 
             public void visitFile(FileVisitDetails fileDetails) {
                 fileDetails.getFile();
+                hasContent.set(true);
             }
         });
+        return hasContent.get();
     }
 
     @Override
@@ -122,6 +127,11 @@ public abstract class AbstractFileTree extends AbstractFileCollection implements
             return fileTree.getDisplayName();
         }
 
+        @Override
+        public TaskDependency getBuildDependencies() {
+            return fileTree.getBuildDependencies();
+        }
+
         public FileTree visit(final FileVisitor visitor) {
             fileTree.visit(new FileVisitor() {
                 public void visitDir(FileVisitDetails dirDetails) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AntFileCollectionMatchingTaskBuilder.groovy b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AntFileCollectionMatchingTaskBuilder.groovy
index 4bcbd54..8e402cc 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AntFileCollectionMatchingTaskBuilder.groovy
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AntFileCollectionMatchingTaskBuilder.groovy
@@ -16,20 +16,22 @@
 package org.gradle.api.internal.file
 
 import org.gradle.api.tasks.AntBuilderAware
+import org.gradle.api.internal.file.collections.DirectoryFileTree
 
 class AntFileCollectionMatchingTaskBuilder implements AntBuilderAware {
-    private final Iterable<DefaultConfigurableFileTree> fileTrees
+    private final Iterable<DirectoryFileTree> fileTrees
 
-    def AntFileCollectionMatchingTaskBuilder(Iterable<DefaultConfigurableFileTree> fileTrees) {
+    def AntFileCollectionMatchingTaskBuilder(Iterable<DirectoryFileTree> fileTrees) {
         this.fileTrees = fileTrees
     }
 
     def addToAntBuilder(Object node, String childNodeName) {
-        fileTrees.each {DefaultConfigurableFileTree fileTree ->
+        def existing = fileTrees.findAll { it.dir.exists()}
+        existing.each {DirectoryFileTree fileTree ->
             node."$childNodeName"(location: fileTree.dir)
         }
         node.or {
-            fileTrees.each {DefaultConfigurableFileTree fileTree ->
+            existing.each {DirectoryFileTree fileTree ->
                 and {
                     gradleBaseDirSelector(baseDir: fileTree.dir)
                     fileTree.patternSet.addToAntBuilder(node, null)
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AntFileSetBuilder.groovy b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AntFileSetBuilder.groovy
new file mode 100644
index 0000000..1166196
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AntFileSetBuilder.groovy
@@ -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.api.internal.file
+
+import org.gradle.api.internal.file.collections.DirectoryFileTree
+import org.gradle.api.tasks.AntBuilderAware
+
+class AntFileSetBuilder implements AntBuilderAware {
+    private final Iterable<DirectoryFileTree> trees
+
+    AntFileSetBuilder(Iterable<DirectoryFileTree> trees) {
+        this.trees = trees
+    }
+
+    def addToAntBuilder(def node, String nodeName) {
+        trees.each { tree ->
+            if (!tree.dir.exists()) {
+                return
+            }
+            node."${nodeName ?: 'fileset'}"(dir: tree.dir) {
+                tree.patternSet.addToAntBuilder(node, null)
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AntFileTreeBuilder.groovy b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AntFileTreeBuilder.groovy
index ab0b326..53c81a6 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AntFileTreeBuilder.groovy
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AntFileTreeBuilder.groovy
@@ -28,6 +28,7 @@ class AntFileTreeBuilder implements AntBuilderAware {
     def addToAntBuilder(node, String childNodeName = null) {
         node."${childNodeName ?: 'resources'}"() {
             files.each { String name, File file ->
+                // gradleFileResource type is mapped to AntFileResource
                 gradleFileResource(file: file.absolutePath, name: name)
             }
         }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/CompositeFileCollection.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/CompositeFileCollection.java
index 3e48568..2d554f0 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/CompositeFileCollection.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/CompositeFileCollection.java
@@ -16,8 +16,10 @@
 
 package org.gradle.api.internal.file;
 
+import org.gradle.api.Buildable;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.file.FileTree;
+import org.gradle.api.internal.file.collections.*;
 import org.gradle.api.internal.tasks.AbstractTaskDependency;
 import org.gradle.api.internal.tasks.TaskDependencyResolveContext;
 import org.gradle.api.specs.Spec;
@@ -30,7 +32,7 @@ import java.util.*;
  * A {@link org.gradle.api.file.FileCollection} which contains the union of the given source collections. Maintains file
  * ordering.
  */
-public abstract class CompositeFileCollection extends AbstractFileCollection {
+public abstract class CompositeFileCollection extends AbstractFileCollection implements FileCollectionContainer {
     public Set<File> getFiles() {
         Set<File> files = new LinkedHashSet<File>();
         for (FileCollection collection : getSourceCollections()) {
@@ -67,8 +69,8 @@ public abstract class CompositeFileCollection extends AbstractFileCollection {
     }
 
     @Override
-    protected Collection<DefaultConfigurableFileTree> getAsFileTrees() {
-        List<DefaultConfigurableFileTree> fileTree = new ArrayList<DefaultConfigurableFileTree>();
+    protected Collection<DirectoryFileTree> getAsFileTrees() {
+        List<DirectoryFileTree> fileTree = new ArrayList<DirectoryFileTree>();
         for (FileCollection source : getSourceCollections()) {
             AbstractFileCollection collection = (AbstractFileCollection) source;
             fileTree.addAll(collection.getAsFileTrees());
@@ -80,21 +82,16 @@ public abstract class CompositeFileCollection extends AbstractFileCollection {
     public FileTree getAsFileTree() {
         return new CompositeFileTree() {
             @Override
-            protected void addSourceCollections(Collection<FileCollection> sources) {
-                for (FileCollection collection : CompositeFileCollection.this.getSourceCollections()) {
-                    sources.add(collection.getAsFileTree());
-                }
+            public void resolve(FileCollectionResolveContext context) {
+                ResolvableFileCollectionResolveContext nested = context.newContext();
+                CompositeFileCollection.this.resolve(nested);
+                context.add(nested.resolveAsFileTrees());
             }
 
             @Override
             public String getDisplayName() {
                 return CompositeFileCollection.this.getDisplayName();
             }
-
-            @Override
-            public TaskDependency getBuildDependencies() {
-                return CompositeFileCollection.this.getBuildDependencies();
-            }
         };
     }
 
@@ -102,9 +99,9 @@ public abstract class CompositeFileCollection extends AbstractFileCollection {
     public FileCollection filter(final Spec<? super File> filterSpec) {
         return new CompositeFileCollection() {
             @Override
-            protected void addSourceCollections(Collection<FileCollection> sources) {
+            public void resolve(FileCollectionResolveContext context) {
                 for (FileCollection collection : CompositeFileCollection.this.getSourceCollections()) {
-                    sources.add(collection.filter(filterSpec));
+                    context.add(collection.filter(filterSpec));
                 }
             }
 
@@ -134,16 +131,18 @@ public abstract class CompositeFileCollection extends AbstractFileCollection {
      * @param context The context to add dependencies to.
      */
     protected void addDependencies(TaskDependencyResolveContext context) {
-        for (FileCollection collection : getSourceCollections()) {
-            context.add(collection);
+        BuildDependenciesOnlyFileCollectionResolveContext fileContext = new BuildDependenciesOnlyFileCollectionResolveContext();
+        resolve(fileContext);
+        for (Buildable buildable : fileContext.resolveAsBuildables()) {
+            context.add(buildable);
         }
     }
 
     protected List<? extends FileCollection> getSourceCollections() {
-        List<FileCollection> collections = new ArrayList<FileCollection>();
-        addSourceCollections(collections);
-        return collections;
+        DefaultFileCollectionResolveContext context = new DefaultFileCollectionResolveContext();
+        resolve(context);
+        return context.resolveAsFileCollections();
     }
 
-    protected abstract void addSourceCollections(Collection<FileCollection> sources);
+    public abstract void resolve(FileCollectionResolveContext context);
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/CompositeFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/CompositeFileTree.java
index c2f33a1..2fb6fb1 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/CompositeFileTree.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/CompositeFileTree.java
@@ -16,13 +16,13 @@
 package org.gradle.api.internal.file;
 
 import groovy.lang.Closure;
-import org.gradle.api.file.FileCollection;
 import org.gradle.api.file.FileTree;
 import org.gradle.api.file.FileVisitor;
+import org.gradle.api.internal.file.collections.FileCollectionResolveContext;
+import org.gradle.api.internal.file.collections.ResolvableFileCollectionResolveContext;
 import org.gradle.api.tasks.TaskDependency;
 import org.gradle.api.tasks.util.PatternFilterable;
 
-import java.util.Collection;
 import java.util.List;
 
 public abstract class CompositeFileTree extends CompositeFileCollection implements FileTree {
@@ -86,12 +86,14 @@ public abstract class CompositeFileTree extends CompositeFileCollection implemen
         }
 
         @Override
-        protected void addSourceCollections(Collection<FileCollection> sources) {
-            for (FileTree set : CompositeFileTree.this.getSourceCollections()) {
+        public void resolve(FileCollectionResolveContext context) {
+            ResolvableFileCollectionResolveContext nestedContext = context.newContext();
+            CompositeFileTree.this.resolve(nestedContext);
+            for (FileTree set : nestedContext.resolveAsFileTrees()) {
                 if (closure != null) {
-                    sources.add(set.matching(closure));
+                    context.add(set.matching(closure));
                 } else {
-                    sources.add(set.matching(patterns));
+                    context.add(set.matching(patterns));
                 }
             }
         }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultConfigurableFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultConfigurableFileTree.java
deleted file mode 100644
index 22ca21d..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultConfigurableFileTree.java
+++ /dev/null
@@ -1,221 +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.file;
-
-import groovy.lang.Closure;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.file.*;
-import org.gradle.api.internal.file.copy.CopyActionImpl;
-import org.gradle.api.internal.file.copy.FileCopyActionImpl;
-import org.gradle.api.internal.file.copy.FileCopySpecVisitor;
-import org.gradle.api.internal.tasks.DefaultTaskDependency;
-import org.gradle.api.internal.tasks.TaskResolver;
-import org.gradle.api.specs.Spec;
-import org.gradle.api.tasks.TaskDependency;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.api.tasks.util.PatternFilterable;
-import org.gradle.api.tasks.util.PatternSet;
-import org.gradle.util.ConfigureUtil;
-
-import java.io.File;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Pattern;
-
-/**
- * @author Hans Dockter
- */
-public class DefaultConfigurableFileTree extends AbstractFileTree implements ConfigurableFileTree {
-    private PatternSet patternSet = new PatternSet();
-    private Object dir;
-    private final FileResolver resolver;
-    private final DefaultTaskDependency buildDependency;
-    private TaskResolver taskResolver;
-
-    public DefaultConfigurableFileTree(Object dir, FileResolver resolver, TaskResolver taskResolver) {
-        this(Collections.singletonMap("dir", dir), resolver, taskResolver);
-    }
-
-    public DefaultConfigurableFileTree(Map<String, ?> args, FileResolver resolver, TaskResolver taskResolver) {
-        this.resolver = resolver != null ? resolver : new IdentityFileResolver();
-        ConfigureUtil.configureByMap(args, this);
-        buildDependency = new DefaultTaskDependency(taskResolver);
-    }
-
-    public PatternSet getPatternSet() {
-        return patternSet;
-    }
-
-    public void setPatternSet(PatternSet patternSet) {
-        this.patternSet = patternSet;
-    }
-
-    public DefaultConfigurableFileTree setDir(Object dir) {
-        from(dir);
-        return this;
-    }
-
-    public File getDir() {
-        if (dir == null) {
-            throw new InvalidUserDataException("A base directory must be specified in the task or via a method argument!");
-        }
-        return resolver.resolve(dir);
-    }
-
-    public DefaultConfigurableFileTree from(Object dir) {
-        this.dir = dir;
-        return this;
-    }
-
-    public String getDisplayName() {
-        return String.format("file set '%s'", dir);
-    }
-
-    public FileTree matching(PatternFilterable patterns) {
-        PatternSet patternSet = this.patternSet.intersect();
-        patternSet.copyFrom(patterns);
-        DefaultConfigurableFileTree filtered = new DefaultConfigurableFileTree(getDir(), resolver, taskResolver);
-        filtered.setPatternSet(patternSet);
-        return filtered;
-    }
-
-    public DefaultConfigurableFileTree visit(FileVisitor visitor) {
-        DefaultDirectoryWalker walker = new DefaultDirectoryWalker(visitor);
-        walker.match(patternSet).start(getDir());
-        return this;
-    }
-
-    public WorkResult copy(Closure closure) {
-        CopyActionImpl action = new FileCopyActionImpl(resolver, new FileCopySpecVisitor());
-        action.from(this);
-        ConfigureUtil.configure(closure, action);
-        action.execute();
-        return action;
-    }
-
-    public Set<String> getIncludes() {
-        return patternSet.getIncludes();
-    }
-
-    public DefaultConfigurableFileTree setIncludes(Iterable<String> includes) {
-        patternSet.setIncludes(includes);
-        return this;
-    }
-
-    public Set<String> getExcludes() {
-        return patternSet.getExcludes();
-    }
-
-    public DefaultConfigurableFileTree setExcludes(Iterable<String> excludes) {
-        patternSet.setExcludes(excludes);
-        return this;
-    }
-
-    public DefaultConfigurableFileTree include(String ... includes) {
-        patternSet.include(includes);
-        return this;
-    }
-
-    public DefaultConfigurableFileTree include(Iterable<String> includes) {
-        patternSet.include(includes);
-        return this;
-    }
-
-    public DefaultConfigurableFileTree include(Closure includeSpec) {
-        patternSet.include(includeSpec);
-        return this;
-    }
-
-    public DefaultConfigurableFileTree include(Spec<FileTreeElement> includeSpec) {
-        patternSet.include(includeSpec);
-        return this;
-    }
-
-    public DefaultConfigurableFileTree exclude(String ... excludes) {
-        patternSet.exclude(excludes);
-        return this;
-    }
-
-    public DefaultConfigurableFileTree exclude(Iterable<String> excludes) {
-        patternSet.exclude(excludes);
-        return this;
-    }
-
-    public DefaultConfigurableFileTree exclude(Spec<FileTreeElement> excludeSpec) {
-        patternSet.exclude(excludeSpec);
-        return this;
-    }
-
-    public DefaultConfigurableFileTree exclude(Closure excludeSpec) {
-        patternSet.exclude(excludeSpec);
-        return this;
-    }
-
-    public boolean contains(File file) {
-        String prefix = getDir().getAbsolutePath() + File.separator;
-        if (!file.getAbsolutePath().startsWith(prefix)) {
-            return false;
-        }
-        if (!file.isFile()) {
-            return false;
-        }
-        RelativePath path = new RelativePath(true, file.getAbsolutePath().substring(prefix.length()).split(
-                Pattern.quote(File.separator)));
-        return patternSet.getAsSpec().isSatisfiedBy(new DefaultFileTreeElement(file, path));
-    }
-
-    protected void addAsFileSet(Object builder, String nodeName) {
-        File dir = getDir();
-        if (!dir.exists()) {
-            return;
-        }
-        doAddFileSet(builder, dir, nodeName);
-    }
-
-    protected void addAsResourceCollection(Object builder, String nodeName) {
-        addAsFileSet(builder, nodeName);
-    }
-
-    protected Collection<DefaultConfigurableFileTree> getAsFileTrees() {
-        return getDir().exists() ? Collections.singletonList(this) : Collections.<DefaultConfigurableFileTree>emptyList();
-    }
-
-    protected Object doAddFileSet(Object builder, File dir, String nodeName) {
-        new FileSetHelper().addToAntBuilder(builder, dir, patternSet, nodeName);
-        return this;
-    }
-
-    public ConfigurableFileTree builtBy(Object... tasks) {
-        buildDependency.add(tasks);
-        return this;
-    }
-
-    public Set<Object> getBuiltBy() {
-        return buildDependency.getValues();
-    }
-
-    public ConfigurableFileTree setBuiltBy(Iterable<?> tasks) {
-        buildDependency.setValues(tasks);
-        return this;
-    }
-
-    @Override
-    public TaskDependency getBuildDependencies() {
-        return buildDependency;
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultDirectoryWalker.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultDirectoryWalker.java
deleted file mode 100644
index ed21488..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultDirectoryWalker.java
+++ /dev/null
@@ -1,149 +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.file;
-
-import org.gradle.api.GradleException;
-import org.gradle.api.file.FileTreeElement;
-import org.gradle.api.file.FileVisitDetails;
-import org.gradle.api.file.FileVisitor;
-import org.gradle.api.file.RelativePath;
-import org.gradle.api.specs.Spec;
-import org.gradle.api.specs.Specs;
-import org.gradle.api.tasks.util.PatternSet;
-import org.gradle.util.GFileUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * Directory walker supporting {@link Spec}s for includes and excludes.
- * The file system is traversed breadth first - all files in a directory will be
- * visited before any child directory is visited.
- *
- * A file or directory will only be visited if it matches all includes and no
- * excludes.
- *
- * @author Steve Appling
- */
-public class DefaultDirectoryWalker implements DirectoryWalker {
-    private static Logger logger = LoggerFactory.getLogger(DefaultDirectoryWalker.class);
-
-    private FileVisitor visitor;
-    private Spec<FileTreeElement> spec;
-    private boolean depthFirst;
-
-    public DefaultDirectoryWalker(FileVisitor visitor) {
-        spec = Specs.satisfyAll();
-        this.visitor = visitor;
-    }
-
-    public DefaultDirectoryWalker match(PatternSet patternSet) {
-        spec = patternSet.getAsSpec();
-        return this;
-    }
-
-    /**
-     * Process the specified file or directory.  Note that the startFile parameter
-     * may be either a directory or a file.  If it is a directory, then it's contents
-     * (but not the directory itself) will be checked with isAllowed and notified to
-     * the listener.  If it is a file, the file will be checked and notified.
-     */
-    public void start(File startFile) {
-        File root = GFileUtils.canonicalise(startFile);
-        AtomicBoolean stopFlag = new AtomicBoolean();
-        if (root.exists()) {
-            if (root.isFile()) {
-                processSingleFile(root, stopFlag);
-            } else {
-               walkDir(root, new RelativePath(false), stopFlag);
-            }
-        } else {
-            logger.info("file or directory '"+startFile.toString()+"', not found");
-        }
-    }
-
-    private void processSingleFile(File file, AtomicBoolean stopFlag) {
-        RelativePath path = new RelativePath(true, file.getName());
-        FileVisitDetailsImpl details = new FileVisitDetailsImpl(file, path, stopFlag);
-        if (isAllowed(details)) {
-            visitor.visitFile(details);
-        }
-    }
-
-    private void walkDir(File file, RelativePath path, AtomicBoolean stopFlag) {
-        File[] children = file.listFiles();
-        if (children == null) {
-            if (file.isDirectory() && !file.canRead()) {
-                throw new GradleException(String.format("Could not list contents of directory '%s' as it is not readable.", file));
-            }
-            // else, might be a link which points to nothing, or has been removed while we're visiting, or ...
-            throw new GradleException(String.format("Could not list contents of '%s'.", file));
-        }
-        List<FileVisitDetailsImpl> dirs = new ArrayList<FileVisitDetailsImpl>();
-        for (int i = 0; !stopFlag.get() && i < children.length; i++) {
-            File child = children[i];
-            boolean isFile = child.isFile();
-            RelativePath childPath = path.append(isFile, child.getName());
-            FileVisitDetailsImpl details = new FileVisitDetailsImpl(child, childPath, stopFlag);
-            if (isAllowed(details)) {
-                if (isFile) {
-                    visitor.visitFile(details);
-                } else {
-                    dirs.add(details);
-                }
-            }
-        }
-
-        // now handle dirs
-        for (int i = 0; !stopFlag.get() && i < dirs.size(); i++) {
-            FileVisitDetailsImpl dir = dirs.get(i);
-            if (depthFirst) {
-                walkDir(dir.getFile(), dir.getRelativePath(), stopFlag);
-                visitor.visitDir(dir);
-            } else {
-                visitor.visitDir(dir);
-                walkDir(dir.getFile(), dir.getRelativePath(), stopFlag);
-            }
-        }
-    }
-
-    boolean isAllowed(FileTreeElement element) {
-        return spec.isSatisfiedBy(element);
-    }
-
-    public DirectoryWalker depthFirst() {
-        depthFirst = true;
-        return this;
-    }
-
-    private static class FileVisitDetailsImpl extends DefaultFileTreeElement implements FileVisitDetails {
-        private final AtomicBoolean stop;
-
-        private FileVisitDetailsImpl(File file, RelativePath relativePath, AtomicBoolean stop) {
-            super(file, relativePath);
-            this.stop = stop;
-        }
-
-        public void stopVisiting() {
-            stop.set(true);
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultFileOperations.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultFileOperations.java
index ea37c1e..213087e 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultFileOperations.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultFileOperations.java
@@ -1,146 +1,149 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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 groovy.lang.Closure;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.PathValidation;
-import org.gradle.api.file.*;
-import org.gradle.api.internal.file.archive.TarFileTree;
-import org.gradle.api.internal.file.archive.ZipFileTree;
-import org.gradle.api.internal.file.copy.*;
-import org.gradle.api.internal.tasks.TaskResolver;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.process.ExecResult;
-import org.gradle.process.internal.DefaultExecAction;
-import org.gradle.process.internal.DefaultJavaExecAction;
-import org.gradle.process.internal.ExecAction;
-import org.gradle.process.internal.JavaExecAction;
-import org.gradle.util.ConfigureUtil;
-
-import java.io.File;
-import java.net.URI;
-import java.util.Collections;
-import java.util.Map;
-
-import static org.gradle.util.ConfigureUtil.configure;
-
-public class DefaultFileOperations implements FileOperations {
-    private final FileResolver fileResolver;
-    private final TaskResolver taskResolver;
-    private final TemporaryFileProvider temporaryFileProvider;
-    private DeleteAction deleteAction;
-
-    public DefaultFileOperations(FileResolver fileResolver, TaskResolver taskResolver, TemporaryFileProvider temporaryFileProvider) {
-        this.fileResolver = fileResolver;
-        this.taskResolver = taskResolver;
-        this.temporaryFileProvider = temporaryFileProvider;
-        this.deleteAction = new DeleteActionImpl(fileResolver);
-    }
-
-    public File file(Object path) {
-        return fileResolver.resolve(path);
-    }
-
-    public File file(Object path, PathValidation validation) {
-        return fileResolver.resolve(path, validation);
-    }
-
-    public URI uri(Object path) {
-        return fileResolver.resolveUri(path);
-    }
-    
-    public ConfigurableFileCollection files(Object... paths) {
-        return new PathResolvingFileCollection(fileResolver, taskResolver, paths);
-    }
-
-    public ConfigurableFileCollection files(Object paths, Closure configureClosure) {
-        return configure(configureClosure, files(paths));
-    }
-
-    public ConfigurableFileTree fileTree(Object baseDir) {
-        return new DefaultConfigurableFileTree(baseDir, fileResolver, taskResolver);
-    }
-
-    public DefaultConfigurableFileTree fileTree(Map<String, ?> args) {
-        return new DefaultConfigurableFileTree(args, fileResolver, taskResolver);
-    }
-
-    public DefaultConfigurableFileTree fileTree(Closure closure) {
-        return configure(closure, new DefaultConfigurableFileTree(Collections.emptyMap(), fileResolver, taskResolver));
-    }
-
-    public FileTree zipTree(Object zipPath) {
-        return new ZipFileTree(file(zipPath), getExpandDir());
-    }
-
-    public FileTree tarTree(Object tarPath) {
-        return new TarFileTree(file(tarPath), getExpandDir());
-    }
-
-    private File getExpandDir() {
-        return temporaryFileProvider.newTemporaryFile("expandedArchives");
-    }
-
-    public String relativePath(Object path) {
-        return fileResolver.resolveAsRelativePath(path);
-    }
-
-    public File mkdir(Object path) {
-        File dir = fileResolver.resolve(path);
-        if (dir.isFile()) {
-            throw new InvalidUserDataException(String.format("Can't create directory. The path=%s points to an existing file.", path));
-        }
-        dir.mkdirs();
-        return dir;
-    }
-
-    public boolean delete(Object... paths) {
-        return deleteAction.delete(paths);
-    }
-
-    public WorkResult copy(Closure closure) {
-        CopyActionImpl action = configure(closure, new FileCopyActionImpl(fileResolver, new FileCopySpecVisitor()));
-        action.execute();
-        return action;
-    }
-
-    public CopySpec copySpec(Closure closure) {
-        return configure(closure, new CopySpecImpl(fileResolver));
-    }
-
-    public FileResolver getFileResolver() {
-        return fileResolver;
-    }
-
-    public DeleteAction getDeleteAction() {
-        return deleteAction;
-    }
-
-    public void setDeleteAction(DeleteAction deleteAction) {
-        this.deleteAction = deleteAction;
-    }
-
-    public ExecResult javaexec(Closure cl) {
-        JavaExecAction javaExecAction = ConfigureUtil.configure(cl, new DefaultJavaExecAction(fileResolver));
-        return javaExecAction.execute();
-    }
-
-    public ExecResult exec(Closure cl) {
-        ExecAction execAction = ConfigureUtil.configure(cl, new DefaultExecAction(fileResolver));
-        return execAction.execute();
-    }
-}
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 groovy.lang.Closure;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.PathValidation;
+import org.gradle.api.file.*;
+import org.gradle.api.internal.file.archive.TarFileTree;
+import org.gradle.api.internal.file.archive.ZipFileTree;
+import org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection;
+import org.gradle.api.internal.file.collections.DefaultConfigurableFileTree;
+import org.gradle.api.internal.file.collections.FileTreeAdapter;
+import org.gradle.api.internal.file.copy.*;
+import org.gradle.api.internal.tasks.TaskResolver;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.process.ExecResult;
+import org.gradle.process.internal.DefaultExecAction;
+import org.gradle.process.internal.DefaultJavaExecAction;
+import org.gradle.process.internal.ExecAction;
+import org.gradle.process.internal.JavaExecAction;
+import org.gradle.util.ConfigureUtil;
+
+import java.io.File;
+import java.net.URI;
+import java.util.Collections;
+import java.util.Map;
+
+import static org.gradle.util.ConfigureUtil.configure;
+
+public class DefaultFileOperations implements FileOperations {
+    private final FileResolver fileResolver;
+    private final TaskResolver taskResolver;
+    private final TemporaryFileProvider temporaryFileProvider;
+    private DeleteAction deleteAction;
+
+    public DefaultFileOperations(FileResolver fileResolver, TaskResolver taskResolver, TemporaryFileProvider temporaryFileProvider) {
+        this.fileResolver = fileResolver;
+        this.taskResolver = taskResolver;
+        this.temporaryFileProvider = temporaryFileProvider;
+        this.deleteAction = new DeleteActionImpl(fileResolver);
+    }
+
+    public File file(Object path) {
+        return fileResolver.resolve(path);
+    }
+
+    public File file(Object path, PathValidation validation) {
+        return fileResolver.resolve(path, validation);
+    }
+
+    public URI uri(Object path) {
+        return fileResolver.resolveUri(path);
+    }
+    
+    public ConfigurableFileCollection files(Object... paths) {
+        return new DefaultConfigurableFileCollection(fileResolver, taskResolver, paths);
+    }
+
+    public ConfigurableFileCollection files(Object paths, Closure configureClosure) {
+        return configure(configureClosure, files(paths));
+    }
+
+    public ConfigurableFileTree fileTree(Object baseDir) {
+        return new DefaultConfigurableFileTree(baseDir, fileResolver, taskResolver);
+    }
+
+    public ConfigurableFileTree fileTree(Map<String, ?> args) {
+        return new DefaultConfigurableFileTree(args, fileResolver, taskResolver);
+    }
+
+    public ConfigurableFileTree fileTree(Closure closure) {
+        return configure(closure, new DefaultConfigurableFileTree(Collections.emptyMap(), fileResolver, taskResolver));
+    }
+
+    public FileTree zipTree(Object zipPath) {
+        return new FileTreeAdapter(new ZipFileTree(file(zipPath), getExpandDir()));
+    }
+
+    public FileTree tarTree(Object tarPath) {
+        return new FileTreeAdapter(new TarFileTree(file(tarPath), getExpandDir()));
+    }
+
+    private File getExpandDir() {
+        return temporaryFileProvider.newTemporaryFile("expandedArchives");
+    }
+
+    public String relativePath(Object path) {
+        return fileResolver.resolveAsRelativePath(path);
+    }
+
+    public File mkdir(Object path) {
+        File dir = fileResolver.resolve(path);
+        if (dir.isFile()) {
+            throw new InvalidUserDataException(String.format("Can't create directory. The path=%s points to an existing file.", path));
+        }
+        dir.mkdirs();
+        return dir;
+    }
+
+    public boolean delete(Object... paths) {
+        return deleteAction.delete(paths);
+    }
+
+    public WorkResult copy(Closure closure) {
+        CopyActionImpl action = configure(closure, new FileCopyActionImpl(fileResolver, new FileCopySpecVisitor()));
+        action.execute();
+        return action;
+    }
+
+    public CopySpec copySpec(Closure closure) {
+        return configure(closure, new CopySpecImpl(fileResolver));
+    }
+
+    public FileResolver getFileResolver() {
+        return fileResolver;
+    }
+
+    public DeleteAction getDeleteAction() {
+        return deleteAction;
+    }
+
+    public void setDeleteAction(DeleteAction deleteAction) {
+        this.deleteAction = deleteAction;
+    }
+
+    public ExecResult javaexec(Closure cl) {
+        JavaExecAction javaExecAction = ConfigureUtil.configure(cl, new DefaultJavaExecAction(fileResolver));
+        return javaExecAction.execute();
+    }
+
+    public ExecResult exec(Closure cl) {
+        ExecAction execAction = ConfigureUtil.configure(cl, new DefaultExecAction(fileResolver));
+        return execAction.execute();
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultSourceDirectorySet.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultSourceDirectorySet.java
index affde9e..1c7a04d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultSourceDirectorySet.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultSourceDirectorySet.java
@@ -15,40 +15,39 @@
  */
 package org.gradle.api.internal.file;
 
+import groovy.lang.Closure;
 import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.specs.Spec;
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.file.SourceDirectorySet;
+import org.gradle.api.file.DirectoryTree;
 import org.gradle.api.file.FileTreeElement;
+import org.gradle.api.file.SourceDirectorySet;
+import org.gradle.api.internal.file.collections.DirectoryFileTree;
+import org.gradle.api.internal.file.collections.FileCollectionResolveContext;
+import org.gradle.api.specs.Spec;
 import org.gradle.api.tasks.util.PatternFilterable;
 import org.gradle.api.tasks.util.PatternSet;
+import org.gradle.util.GUtil;
 
 import java.io.File;
-import java.util.Collection;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-import groovy.lang.Closure;
+import java.util.*;
 
 public class DefaultSourceDirectorySet extends CompositeFileTree implements SourceDirectorySet {
-    private final PathResolvingFileCollection srcDirs;
+    private final List<Object> source = new ArrayList<Object>();
     private final String displayName;
-    private final FileResolver resolver;
-    private final PatternFilterable patterns = new PatternSet();
-    private final PatternFilterable filter = new PatternSet();
-
-    public DefaultSourceDirectorySet(FileResolver fileResolver) {
-        this("source set", fileResolver);
-    }
+    private final FileResolver fileResolver;
+    private final PatternSet patterns = new PatternSet();
+    private final PatternSet filter = new PatternSet();
 
     public DefaultSourceDirectorySet(String displayName, FileResolver fileResolver) {
         this.displayName = displayName;
-        this.resolver = fileResolver;
-        srcDirs = new PathResolvingFileCollection(fileResolver, null);
+        this.fileResolver = fileResolver;
     }
 
     public Set<File> getSrcDirs() {
-        return srcDirs.getFiles();
+        Set<File> dirs = new LinkedHashSet<File>();
+        for (DirectoryTree tree : getSrcDirTrees()) {
+            dirs.add(tree.getDir());
+        }
+        return dirs;
     }
 
     public Set<String> getIncludes() {
@@ -113,12 +112,40 @@ public class DefaultSourceDirectorySet extends CompositeFileTree implements Sour
         return filter;
     }
 
+    public Set<DirectoryTree> getSrcDirTrees() {
+        // This implementation is broken. It does not consider include and exclude patterns
+        Map<File, DirectoryTree> trees = new LinkedHashMap<File, DirectoryTree>();
+        for (DirectoryTree tree : doGetSrcDirTrees()) {
+            if (!trees.containsKey(tree.getDir())) {
+                trees.put(tree.getDir(), tree);
+            }
+        }
+        return new LinkedHashSet<DirectoryTree>(trees.values());
+    }
+
+    private Set<DirectoryTree> doGetSrcDirTrees() {
+        Set<DirectoryTree> result = new LinkedHashSet<DirectoryTree>();
+        for (Object path : source) {
+            if (path instanceof SourceDirectorySet) {
+                SourceDirectorySet nested = (SourceDirectorySet) path;
+                result.addAll(nested.getSrcDirTrees());
+            } else {
+                File srcDir = fileResolver.resolve(path);
+                if (srcDir.exists() && !srcDir.isDirectory()) {
+                    throw new InvalidUserDataException(String.format("Source directory '%s' is not a directory.", srcDir));
+                }
+                result.add(new DirectoryFileTree(srcDir, patterns));
+            }
+        }
+        return result;
+    }
+
     @Override
-    protected void addSourceCollections(Collection<FileCollection> sources) {
-        for (File sourceDir : getExistingSourceDirs()) {
-            DefaultConfigurableFileTree fileset = new DefaultConfigurableFileTree(sourceDir, resolver, null);
-            fileset.getPatternSet().copyFrom(patterns);
-            sources.add(fileset.matching(filter));
+    public void resolve(FileCollectionResolveContext context) {
+        for (DirectoryTree directoryTree : getSrcDirTrees()) {
+            if (directoryTree.getDir().isDirectory()) {
+                context.add(((DirectoryFileTree) directoryTree).filter(filter));
+            }
         }
     }
 
@@ -127,31 +154,26 @@ public class DefaultSourceDirectorySet extends CompositeFileTree implements Sour
         return displayName;
     }
 
-    protected Set<File> getExistingSourceDirs() {
-        Set<File> existingSourceDirs = new LinkedHashSet<File>();
-        for (File srcDir : srcDirs) {
-            if (srcDir.isDirectory()) {
-                existingSourceDirs.add(srcDir);
-            } else if (srcDir.exists()) {
-                throw new InvalidUserDataException(String.format("Source directory '%s' is not a directory.", srcDir));
-            }
-        }
-        return existingSourceDirs;
-    }
-
     public SourceDirectorySet srcDir(Object srcDir) {
-        srcDirs.from(srcDir);
+        source.add(srcDir);
         return this;
     }
 
     public SourceDirectorySet srcDirs(Object... srcDirs) {
-        this.srcDirs.from(srcDirs);
+        for (Object srcDir : srcDirs) {
+            source.add(srcDir);
+        }
         return this;
     }
 
     public SourceDirectorySet setSrcDirs(Iterable<Object> srcPaths) {
-        srcDirs.clear();
-        srcDirs.from(srcPaths);
+        source.clear();
+        GUtil.addToCollection(source, srcPaths);
+        return this;
+    }
+
+    public SourceDirectorySet source(SourceDirectorySet source) {
+        this.source.add(source);
         return this;
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DirectoryWalker.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DirectoryWalker.java
deleted file mode 100644
index 2b09b93..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DirectoryWalker.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.api.internal.file;
-
-import org.gradle.api.tasks.util.PatternSet;
-
-import java.io.File;
-
-public interface DirectoryWalker {
-    DirectoryWalker match(PatternSet patternSet);
-
-    void start(File baseDir);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileSetHelper.groovy b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileSetHelper.groovy
deleted file mode 100644
index 487ea51..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileSetHelper.groovy
+++ /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.api.internal.file
-
-import org.gradle.api.tasks.util.PatternSet
-
-class FileSetHelper {
-    def addToAntBuilder(def node, File dir, PatternSet patternSet, String nodeName) {
-        node."${nodeName ?: 'fileset'}"(dir: dir) {
-            patternSet.addToAntBuilder(node, null)
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/MapFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/MapFileTree.java
deleted file mode 100644
index dbb59c3..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/MapFileTree.java
+++ /dev/null
@@ -1,154 +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.file;
-
-import groovy.lang.Closure;
-import org.gradle.api.file.FileTree;
-import org.gradle.api.file.FileVisitDetails;
-import org.gradle.api.file.FileVisitor;
-import org.gradle.api.file.RelativePath;
-
-import java.io.File;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.*;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * A {@link FileTree} which is composed using a mapping from relative path to file source.
- */
-public class MapFileTree extends AbstractFileTree {
-    private final Map<RelativePath, Closure> elements = new LinkedHashMap<RelativePath, Closure>();
-    private final File tmpDir;
-
-    public MapFileTree(File tmpDir) {
-        this.tmpDir = tmpDir;
-    }
-
-    public String getDisplayName() {
-        return "file tree";
-    }
-
-    @Override
-    protected Collection<DefaultConfigurableFileTree> getAsFileTrees() {
-        visitAll();
-        return Collections.singleton(new DefaultConfigurableFileTree(tmpDir, null, null));
-    }
-
-    public FileTree visit(FileVisitor visitor) {
-        AtomicBoolean stopFlag = new AtomicBoolean();
-        Visit visit = new Visit(visitor, stopFlag);
-        for (Map.Entry<RelativePath, Closure> entry : elements.entrySet()) {
-            if (stopFlag.get()) {
-                break;
-            }
-            RelativePath path = entry.getKey();
-            Closure generator = entry.getValue();
-            visit.visit(path, generator);
-        }
-        return this;
-    }
-
-
-    /**
-     * Adds an element to this tree. The given closure is passed an OutputStream which it can use to write the content
-     * of the element to.
-     */
-    public void add(String path, Closure contentClosure) {
-        elements.put(RelativePath.parse(true, path), contentClosure);
-    }
-
-    private class Visit {
-        private final Set<RelativePath> visitedDirs = new LinkedHashSet<RelativePath>();
-        private final FileVisitor visitor;
-        private final AtomicBoolean stopFlag;
-
-        public Visit(FileVisitor visitor, AtomicBoolean stopFlag) {
-            this.visitor = visitor;
-            this.stopFlag = stopFlag;
-        }
-
-        private void visitDirs(RelativePath path, FileVisitor visitor) {
-            if (path == null || path.getParent() == null || !visitedDirs.add(path)) {
-                return;
-            }
-
-            visitDirs(path.getParent(), visitor);
-            visitor.visitDir(new FileVisitDetailsImpl(path, null, stopFlag));
-        }
-
-        public void visit(RelativePath path, Closure generator) {
-            visitDirs(path.getParent(), visitor);
-            visitor.visitFile(new FileVisitDetailsImpl(path, generator, stopFlag));
-        }
-    }
-
-    private class FileVisitDetailsImpl extends AbstractFileTreeElement implements FileVisitDetails {
-        private final RelativePath path;
-        private final Closure generator;
-        private final long lastModified;
-        private final AtomicBoolean stopFlag;
-        private File file;
-
-        public FileVisitDetailsImpl(RelativePath path, Closure generator, AtomicBoolean stopFlag) {
-            this.path = path;
-            this.generator = generator;
-            this.stopFlag = stopFlag;
-            // round to nearest second
-            lastModified = System.currentTimeMillis() / 1000 * 1000;
-        }
-
-        public String getDisplayName() {
-            return path.toString();
-        }
-
-        public void stopVisiting() {
-            stopFlag.set(true);
-        }
-
-        public File getFile() {
-            if (file == null) {
-                file = path.getFile(tmpDir);
-                copyTo(file);
-            }
-            return file;
-        }
-
-        public boolean isDirectory() {
-            return !path.isFile();
-        }
-
-        public long getLastModified() {
-            return lastModified;
-        }
-
-        public long getSize() {
-            return getFile().length();
-        }
-
-        public void copyTo(OutputStream outstr) {
-            generator.call(outstr);
-        }
-
-        public InputStream open() {
-            throw new UnsupportedOperationException();
-        }
-
-        public RelativePath getRelativePath() {
-            return path;
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/PathResolvingFileCollection.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/PathResolvingFileCollection.java
deleted file mode 100644
index efe9dd7..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/PathResolvingFileCollection.java
+++ /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.api.internal.file;
-
-import groovy.lang.Closure;
-import org.gradle.api.file.ConfigurableFileCollection;
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.tasks.DefaultTaskDependency;
-import org.gradle.api.internal.tasks.TaskDependencyResolveContext;
-import org.gradle.api.internal.tasks.TaskResolver;
-import org.gradle.util.UncheckedException;
-
-import java.io.File;
-import java.util.*;
-import java.util.concurrent.Callable;
-
-/**
- * A {@link org.gradle.api.file.FileCollection} which resolves a set of paths relative to a {@link FileResolver}.
- */
-public class PathResolvingFileCollection extends CompositeFileCollection implements ConfigurableFileCollection {
-    private final List<Object> files;
-    private final String displayName;
-    private final FileResolver resolver;
-    private final DefaultTaskDependency buildDependency;
-
-    public PathResolvingFileCollection(FileResolver fileResolver, TaskResolver taskResolver, Object... files) {
-        this("file collection", fileResolver, taskResolver, files);
-    }
-
-    public PathResolvingFileCollection(String displayName, FileResolver fileResolver, TaskResolver taskResolver, Object... files) {
-        this.displayName = displayName;
-        this.resolver = fileResolver;
-        this.files = new ArrayList<Object>(Arrays.asList(files));
-        buildDependency = new DefaultTaskDependency(taskResolver);
-    }
-
-    public PathResolvingFileCollection clear() {
-        files.clear();
-        return this;
-    }
-
-    public String getDisplayName() {
-        return displayName;
-    }
-
-    public List<?> getSources() {
-        return files;
-    }
-
-    public ConfigurableFileCollection from(Object... paths) {
-        for (Object path : paths) {
-            files.add(path);
-        }
-        return this;
-    }
-
-    public ConfigurableFileCollection builtBy(Object... tasks) {
-        buildDependency.add(tasks);
-        return this;
-    }
-
-    public Set<Object> getBuiltBy() {
-        return buildDependency.getValues();
-    }
-
-    public ConfigurableFileCollection setBuiltBy(Iterable<?> tasks) {
-        buildDependency.setValues(tasks);
-        return this;
-    }
-
-    @Override
-    protected void addDependencies(TaskDependencyResolveContext context) {
-        super.addDependencies(context);
-        context.add(buildDependency);
-    }
-
-    @Override
-    protected void addSourceCollections(Collection<FileCollection> sources) {
-        for (Object element : resolveToFilesAndFileCollections()) {
-            if (element instanceof FileCollection) {
-                FileCollection collection = (FileCollection) element;
-                sources.add(collection);
-            } else {
-                File file = (File) element;
-                sources.add(new SingletonFileCollection(file, buildDependency));
-            }
-        }
-    }
-
-    /**
-     * Converts everything in this collection which is not a FileCollection to Files, but leave FileCollections
-     * unresolved.
-     */
-    private List<Object> resolveToFilesAndFileCollections() {
-        List<Object> result = new ArrayList<Object>();
-        LinkedList<Object> queue = new LinkedList<Object>();
-        queue.addAll(files);
-        while (!queue.isEmpty()) {
-            Object first = queue.removeFirst();
-            if (first instanceof FileCollection) {
-                result.add(first);
-            } else if (first instanceof Closure) {
-                Closure closure = (Closure) first;
-                Object closureResult = closure.call();
-                if (closureResult != null) {
-                    queue.addFirst(closureResult);
-                }
-            } else if (first instanceof Collection) {
-                Collection<?> collection = (Collection<?>) first;
-                queue.addAll(0, collection);
-            } else if (first instanceof Object[]) {
-                Object[] array = (Object[]) first;
-                queue.addAll(0, Arrays.asList(array));
-            } else if (first instanceof Callable) {
-                Callable callable = (Callable) first;
-                Object callableResult;
-                try {
-                    callableResult = callable.call();
-                } catch (Exception e) {
-                    throw UncheckedException.asUncheckedException(e);
-                }
-                if (callableResult != null) {
-                    queue.add(0, callableResult);
-                }
-            } else {
-                result.add(resolver.resolve(first));
-            }
-        }
-        return result;
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/SimpleFileCollection.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/SimpleFileCollection.java
deleted file mode 100644
index d6ba01b..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/SimpleFileCollection.java
+++ /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.api.internal.file;
-
-import org.gradle.util.GUtil;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-public class SimpleFileCollection extends AbstractFileCollection {
-    private final Set<File> files;
-
-    public SimpleFileCollection(File... files) {
-        if (files.length > 0) {
-            this.files = new LinkedHashSet<File>(Arrays.asList(files));
-        } else {
-            this.files = Collections.emptySet();
-        }
-    }
-
-    public SimpleFileCollection(Iterable<File> files) {
-        this.files = new LinkedHashSet<File>();
-        GUtil.addToCollection(this.files, files);
-    }
-
-    @Override
-    public String getDisplayName() {
-        return "file collection";
-    }
-
-    public Set<File> getFiles() {
-        return files;
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/SingletonFileCollection.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/SingletonFileCollection.java
deleted file mode 100644
index 8ab3b7c..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/SingletonFileCollection.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.internal.file;
-
-import org.gradle.api.tasks.TaskDependency;
-import org.gradle.api.file.FileTree;
-
-import java.io.File;
-import java.util.Collections;
-import java.util.Set;
-
-class SingletonFileCollection extends AbstractFileCollection {
-    private final File file;
-    private final TaskDependency builtBy;
-
-    public SingletonFileCollection(File file, TaskDependency builtBy) {
-        this.file = file;
-        this.builtBy = builtBy;
-    }
-
-    @Override
-    public TaskDependency getBuildDependencies() {
-        return builtBy;
-    }
-
-    @Override
-    public String getDisplayName() {
-        return String.format("file '%s'", file);
-    }
-
-    public Set<File> getFiles() {
-        return Collections.singleton(file);
-    }
-
-    @Override
-    public FileTree getAsFileTree() {
-        return new SingletonFileTree(file, builtBy);
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/SingletonFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/SingletonFileTree.java
deleted file mode 100644
index c286ef2..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/SingletonFileTree.java
+++ /dev/null
@@ -1,70 +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.file;
-
-import org.gradle.api.file.*;
-import org.gradle.api.tasks.TaskDependency;
-
-import java.io.File;
-import java.util.Collection;
-
-class SingletonFileTree extends CompositeFileTree {
-    private final File file;
-    private final TaskDependency builtBy;
-
-    public SingletonFileTree(File file, TaskDependency builtBy) {
-        this.file = file;
-        this.builtBy = builtBy;
-    }
-
-    @Override
-    public String getDisplayName() {
-        return String.format("file '%s'", file);
-    }
-
-    @Override
-    public TaskDependency getBuildDependencies() {
-        return builtBy;
-    }
-
-    protected void addSourceCollections(Collection<FileCollection> sources) {
-        if (file.isDirectory()) {
-            sources.add(new DefaultConfigurableFileTree(file, null, null));
-        } else if (file.isFile()) {
-            sources.add(new FileFileTree());
-        }
-    }
-
-    private class FileVisitDetailsImpl extends DefaultFileTreeElement implements FileVisitDetails {
-        private FileVisitDetailsImpl() {
-            super(file, new RelativePath(true, file.getName()));
-        }
-
-        public void stopVisiting() {
-        }
-    }
-
-    private class FileFileTree extends AbstractFileTree {
-        public String getDisplayName() {
-            return SingletonFileTree.this.getDisplayName();
-        }
-
-        public FileTree visit(FileVisitor visitor) {
-            visitor.visitFile(new FileVisitDetailsImpl());
-            return this;
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/UnionFileCollection.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/UnionFileCollection.java
index aea2261..3ea8071 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/UnionFileCollection.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/UnionFileCollection.java
@@ -16,33 +16,38 @@
 package org.gradle.api.internal.file;
 
 import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.collections.FileCollectionResolveContext;
 import org.gradle.util.GUtil;
 
 import java.util.*;
 
 public class UnionFileCollection extends CompositeFileCollection {
-    private final Set<FileCollection> sourceCollections;
+    private final Set<FileCollection> source;
 
-    public UnionFileCollection(FileCollection... sourceCollections) {
-        this(Arrays.asList(sourceCollections));
+    public UnionFileCollection(FileCollection... source) {
+        this(Arrays.asList(source));
     }
 
-    public UnionFileCollection(Iterable<? extends FileCollection> sourceCollections) {
-        this.sourceCollections = GUtil.addToCollection(new LinkedHashSet<FileCollection>(), sourceCollections);
+    public UnionFileCollection(Iterable<? extends FileCollection> source) {
+        this.source = GUtil.addToCollection(new LinkedHashSet<FileCollection>(), source);
     }
 
     public String getDisplayName() {
         return "file collection";
     }
 
+    public Set<FileCollection> getSources() {
+        return source;
+    }
+
     @Override
     public FileCollection add(FileCollection collection) throws UnsupportedOperationException {
-        sourceCollections.add(collection);
+        source.add(collection);
         return this;
     }
 
     @Override
-    protected void addSourceCollections(Collection<FileCollection> sources) {
-        sources.addAll(sourceCollections);
+    public void resolve(FileCollectionResolveContext context) {
+        context.add(source);
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/UnionFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/UnionFileTree.java
index 6d1d6a3..f791180 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/UnionFileTree.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/UnionFileTree.java
@@ -17,6 +17,7 @@ package org.gradle.api.internal.file;
 
 import org.gradle.api.file.FileTree;
 import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.collections.FileCollectionResolveContext;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -46,15 +47,14 @@ public class UnionFileTree extends CompositeFileTree {
     }
 
     @Override
-    protected void addSourceCollections(Collection<FileCollection> sources) {
-        sources.addAll(sourceTrees);
+    public void resolve(FileCollectionResolveContext context) {
+        context.add(sourceTrees);
     }
 
     @Override
     public UnionFileTree add(FileCollection source) {
         if (!(source instanceof FileTree)) {
-            throw new UnsupportedOperationException(String.format("Can only add FileTree instances to %s.",
-                    getDisplayName()));
+            throw new UnsupportedOperationException(String.format("Can only add FileTree instances to %s.", getDisplayName()));
         }
         
         sourceTrees.add((FileTree) source);
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/TarFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/TarFileTree.java
index 1bb01f7..89b90fd 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/TarFileTree.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/TarFileTree.java
@@ -15,29 +15,27 @@
  */
 package org.gradle.api.internal.file.archive;
 
+import org.apache.tools.tar.TarEntry;
+import org.apache.tools.tar.TarInputStream;
 import org.gradle.api.GradleException;
 import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.file.FileTree;
 import org.gradle.api.file.FileVisitDetails;
 import org.gradle.api.file.FileVisitor;
 import org.gradle.api.file.RelativePath;
-import org.gradle.api.internal.file.AbstractFileTree;
 import org.gradle.api.internal.file.AbstractFileTreeElement;
-import org.gradle.api.internal.file.DefaultConfigurableFileTree;
+import org.gradle.api.internal.file.collections.DirectoryFileTree;
+import org.gradle.api.internal.file.collections.FileSystemMirroringFileTree;
+import org.gradle.api.internal.file.collections.MinimalFileTree;
 import org.gradle.util.GFileUtils;
 import org.gradle.util.HashUtil;
-import org.apache.tools.tar.TarEntry;
-import org.apache.tools.tar.TarInputStream;
 
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-public class TarFileTree extends AbstractFileTree {
+public class TarFileTree implements MinimalFileTree, FileSystemMirroringFileTree {
     private final File tarFile;
     private final File tmpDir;
 
@@ -51,18 +49,16 @@ public class TarFileTree extends AbstractFileTree {
         return String.format("TAR '%s'", tarFile);
     }
 
-    @Override
-    protected Collection<DefaultConfigurableFileTree> getAsFileTrees() {
-        visitAll();
-        return tarFile.exists() ? Collections.singleton(new DefaultConfigurableFileTree(tmpDir, null, null)) : Collections.<DefaultConfigurableFileTree>emptyList();
+    public DirectoryFileTree getMirror() {
+        return new DirectoryFileTree(tmpDir);
     }
 
-    public FileTree visit(FileVisitor visitor) {
+    public void visit(FileVisitor visitor) {
         if (!tarFile.exists()) {
-            return this;
+            return;
         }
         if (!tarFile.isFile()) {
-            throw new InvalidUserDataException(String.format("Cannot expand %s as it is not a file.", this));
+            throw new InvalidUserDataException(String.format("Cannot expand %s as it is not a file.", getDisplayName()));
         }
 
         AtomicBoolean stopFlag = new AtomicBoolean();
@@ -83,10 +79,8 @@ public class TarFileTree extends AbstractFileTree {
                 inputStream.close();
             }
         } catch (Exception e) {
-            throw new GradleException(String.format("Could not expand %s.", this), e);
+            throw new GradleException(String.format("Could not expand %s.", getDisplayName()), e);
         }
-
-        return this;
     }
 
     private class DetailsImpl extends AbstractFileTreeElement implements FileVisitDetails {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/ZipFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/ZipFileTree.java
index 4c88e0f..17566b5 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/ZipFileTree.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/ZipFileTree.java
@@ -17,25 +17,28 @@ package org.gradle.api.internal.file.archive;
 
 import org.apache.tools.zip.ZipEntry;
 import org.apache.tools.zip.ZipFile;
-import org.gradle.api.UncheckedIOException;
-import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.GradleException;
-import org.gradle.api.internal.file.AbstractFileTree;
-import org.gradle.api.internal.file.AbstractFileTreeElement;
-import org.gradle.api.internal.file.DefaultConfigurableFileTree;
-import org.gradle.api.file.FileTree;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.UncheckedIOException;
 import org.gradle.api.file.FileVisitDetails;
 import org.gradle.api.file.FileVisitor;
 import org.gradle.api.file.RelativePath;
+import org.gradle.api.internal.file.AbstractFileTreeElement;
+import org.gradle.api.internal.file.collections.DirectoryFileTree;
+import org.gradle.api.internal.file.collections.FileSystemMirroringFileTree;
+import org.gradle.api.internal.file.collections.MinimalFileTree;
 import org.gradle.util.HashUtil;
 
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.*;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-public class ZipFileTree extends AbstractFileTree {
+public class ZipFileTree implements MinimalFileTree, FileSystemMirroringFileTree {
     private final File zipFile;
     private final File tmpDir;
 
@@ -49,18 +52,16 @@ public class ZipFileTree extends AbstractFileTree {
         return String.format("ZIP '%s'", zipFile);
     }
 
-    @Override
-    protected Collection<DefaultConfigurableFileTree> getAsFileTrees() {
-        visitAll();
-        return zipFile.exists() ? Collections.singleton(new DefaultConfigurableFileTree(tmpDir, null, null)) : Collections.<DefaultConfigurableFileTree>emptyList();
+    public DirectoryFileTree getMirror() {
+        return new DirectoryFileTree(tmpDir);
     }
 
-    public FileTree visit(FileVisitor visitor) {
+    public void visit(FileVisitor visitor) {
         if (!zipFile.exists()) {
-            return this;
+            return;
         }
         if (!zipFile.isFile()) {
-            throw new InvalidUserDataException(String.format("Cannot expand %s as it is not a file.", this));
+            throw new InvalidUserDataException(String.format("Cannot expand %s as it is not a file.", getDisplayName()));
         }
 
         AtomicBoolean stopFlag = new AtomicBoolean();
@@ -89,10 +90,8 @@ public class ZipFileTree extends AbstractFileTree {
                 zip.close();
             }
         } catch (Exception e) {
-            throw new GradleException(String.format("Could not expand %s.", this), e);
+            throw new GradleException(String.format("Could not expand %s.", getDisplayName()), e);
         }
-
-        return this;
     }
 
     private class DetailsImpl extends AbstractFileTreeElement implements FileVisitDetails {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/BuildDependenciesOnlyFileCollectionResolveContext.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/BuildDependenciesOnlyFileCollectionResolveContext.java
new file mode 100644
index 0000000..b517596
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/BuildDependenciesOnlyFileCollectionResolveContext.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections;
+
+import org.gradle.api.Buildable;
+import org.gradle.api.file.FileTree;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.file.IdentityFileResolver;
+import org.gradle.api.tasks.TaskDependency;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * <p>A {@link FileCollectionResolveContext} which is used to determine the builder dependencies of a file collection hierarchy. Ignores the contents of file
+ * collections when resolving, replacing each file collection with an empty tree with the appropriate build dependencies. This is generally more efficient than
+ * resolving the file collections.
+ *
+ * <p>Nested contexts created by this context will similarly ignore the contents of file collections.
+ */
+public class BuildDependenciesOnlyFileCollectionResolveContext extends DefaultFileCollectionResolveContext {
+    public BuildDependenciesOnlyFileCollectionResolveContext() {
+        super(new IdentityFileResolver(), new BuildableFileTreeInternalConverter(), new BuildableFileTreeInternalConverter());
+    }
+
+    /**
+     * Resolves the contents of this context as a list of atomic {@link Buildable} instances.
+     */
+    public List<? extends Buildable> resolveAsBuildables() {
+        return resolveAsFileCollections();
+    }
+
+    private static class BuildableFileTreeInternalConverter implements Converter<FileTree> {
+        public void convertInto(Object element, Collection<? super FileTree> result, FileResolver resolver) {
+            if (element instanceof DefaultFileCollectionResolveContext) {
+                DefaultFileCollectionResolveContext nestedContext = (DefaultFileCollectionResolveContext) element;
+                result.addAll(nestedContext.resolveAsFileTrees());
+            } else if (element instanceof Buildable) {
+                Buildable buildable = (Buildable) element;
+                result.add(new FileTreeAdapter(new EmptyFileTree(buildable.getBuildDependencies())));
+            } else if (element instanceof TaskDependency) {
+                TaskDependency dependency = (TaskDependency) element;
+                result.add(new FileTreeAdapter(new EmptyFileTree(dependency)));
+            }
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/DefaultConfigurableFileCollection.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/DefaultConfigurableFileCollection.java
new file mode 100644
index 0000000..c5270d6
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/DefaultConfigurableFileCollection.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections;
+
+import org.gradle.api.file.ConfigurableFileCollection;
+import org.gradle.api.internal.file.CompositeFileCollection;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.tasks.DefaultTaskDependency;
+import org.gradle.api.internal.tasks.TaskResolver;
+import org.gradle.util.GUtil;
+
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * A {@link org.gradle.api.file.FileCollection} which resolves a set of paths relative to a {@link org.gradle.api.internal.file.FileResolver}.
+ */
+public class DefaultConfigurableFileCollection extends CompositeFileCollection implements ConfigurableFileCollection {
+    private final Set<Object> files;
+    private final String displayName;
+    private final FileResolver resolver;
+    private final DefaultTaskDependency buildDependency;
+
+    public DefaultConfigurableFileCollection(FileResolver fileResolver, TaskResolver taskResolver, Object... files) {
+        this("file collection", fileResolver, taskResolver, files);
+    }
+
+    public DefaultConfigurableFileCollection(String displayName, FileResolver fileResolver, TaskResolver taskResolver, Object... files) {
+        this.displayName = displayName;
+        this.resolver = fileResolver;
+        this.files = new LinkedHashSet<Object>(Arrays.asList(files));
+        buildDependency = new DefaultTaskDependency(taskResolver);
+    }
+
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    public Set<Object> getFrom() {
+        return files;
+    }
+
+    public void setFrom(Iterable<?> path) {
+        files.clear();
+        files.add(path);
+    }
+
+    public void setFrom(Object... paths) {
+        files.clear();
+        GUtil.addToCollection(files, Arrays.asList(paths));
+    }
+
+    public ConfigurableFileCollection from(Object... paths) {
+        GUtil.addToCollection(files, Arrays.asList(paths));
+        return this;
+    }
+
+    public ConfigurableFileCollection builtBy(Object... tasks) {
+        buildDependency.add(tasks);
+        return this;
+    }
+
+    public Set<Object> getBuiltBy() {
+        return buildDependency.getValues();
+    }
+
+    public ConfigurableFileCollection setBuiltBy(Iterable<?> tasks) {
+        buildDependency.setValues(tasks);
+        return this;
+    }
+
+    @Override
+    public void resolve(FileCollectionResolveContext context) {
+        FileCollectionResolveContext nested = context.push(resolver);
+        if (!buildDependency.getValues().isEmpty()) {
+            nested.add(buildDependency);
+        }
+        nested.add(files);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/DefaultConfigurableFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/DefaultConfigurableFileTree.java
new file mode 100644
index 0000000..21acdcf
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/DefaultConfigurableFileTree.java
@@ -0,0 +1,178 @@
+/*
+ * 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.collections;
+
+import groovy.lang.Closure;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.file.ConfigurableFileTree;
+import org.gradle.api.file.FileTreeElement;
+import org.gradle.api.internal.file.CompositeFileTree;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.file.IdentityFileResolver;
+import org.gradle.api.internal.file.copy.CopyActionImpl;
+import org.gradle.api.internal.file.copy.FileCopyActionImpl;
+import org.gradle.api.internal.file.copy.FileCopySpecVisitor;
+import org.gradle.api.internal.tasks.DefaultTaskDependency;
+import org.gradle.api.internal.tasks.TaskResolver;
+import org.gradle.api.specs.Spec;
+import org.gradle.api.tasks.TaskDependency;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.api.tasks.util.PatternSet;
+import org.gradle.util.ConfigureUtil;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Hans Dockter
+ */
+public class DefaultConfigurableFileTree extends CompositeFileTree implements ConfigurableFileTree {
+    private PatternSet patternSet = new PatternSet();
+    private Object dir;
+    private final FileResolver resolver;
+    private final DefaultTaskDependency buildDependency;
+
+    public DefaultConfigurableFileTree(Object dir, FileResolver resolver, TaskResolver taskResolver) {
+        this(Collections.singletonMap("dir", dir), resolver, taskResolver);
+    }
+
+    public DefaultConfigurableFileTree(Map<String, ?> args, FileResolver resolver, TaskResolver taskResolver) {
+        this.resolver = resolver != null ? resolver : new IdentityFileResolver();
+        ConfigureUtil.configureByMap(args, this);
+        buildDependency = new DefaultTaskDependency(taskResolver);
+    }
+
+    public PatternSet getPatterns() {
+        return patternSet;
+    }
+
+    public DefaultConfigurableFileTree setDir(Object dir) {
+        from(dir);
+        return this;
+    }
+
+    public File getDir() {
+        if (dir == null) {
+            throw new InvalidUserDataException("A base directory must be specified in the task or via a method argument!");
+        }
+        return resolver.resolve(dir);
+    }
+
+    public DefaultConfigurableFileTree from(Object dir) {
+        this.dir = dir;
+        return this;
+    }
+
+    public String getDisplayName() {
+        return String.format("directory '%s'", dir);
+    }
+
+    public WorkResult copy(Closure closure) {
+        CopyActionImpl action = new FileCopyActionImpl(resolver, new FileCopySpecVisitor());
+        action.from(this);
+        ConfigureUtil.configure(closure, action);
+        action.execute();
+        return action;
+    }
+
+    public Set<String> getIncludes() {
+        return patternSet.getIncludes();
+    }
+
+    public DefaultConfigurableFileTree setIncludes(Iterable<String> includes) {
+        patternSet.setIncludes(includes);
+        return this;
+    }
+
+    public Set<String> getExcludes() {
+        return patternSet.getExcludes();
+    }
+
+    public DefaultConfigurableFileTree setExcludes(Iterable<String> excludes) {
+        patternSet.setExcludes(excludes);
+        return this;
+    }
+
+    public DefaultConfigurableFileTree include(String ... includes) {
+        patternSet.include(includes);
+        return this;
+    }
+
+    public DefaultConfigurableFileTree include(Iterable<String> includes) {
+        patternSet.include(includes);
+        return this;
+    }
+
+    public DefaultConfigurableFileTree include(Closure includeSpec) {
+        patternSet.include(includeSpec);
+        return this;
+    }
+
+    public DefaultConfigurableFileTree include(Spec<FileTreeElement> includeSpec) {
+        patternSet.include(includeSpec);
+        return this;
+    }
+
+    public DefaultConfigurableFileTree exclude(String ... excludes) {
+        patternSet.exclude(excludes);
+        return this;
+    }
+
+    public DefaultConfigurableFileTree exclude(Iterable<String> excludes) {
+        patternSet.exclude(excludes);
+        return this;
+    }
+
+    public DefaultConfigurableFileTree exclude(Spec<FileTreeElement> excludeSpec) {
+        patternSet.exclude(excludeSpec);
+        return this;
+    }
+
+    public DefaultConfigurableFileTree exclude(Closure excludeSpec) {
+        patternSet.exclude(excludeSpec);
+        return this;
+    }
+
+    @Override
+    public void resolve(FileCollectionResolveContext context) {
+        File dir = getDir();
+        if (!buildDependency.getValues().isEmpty()) {
+            context.add(buildDependency);
+        }
+        context.add(new DirectoryFileTree(dir, patternSet));
+    }
+
+    public ConfigurableFileTree builtBy(Object... tasks) {
+        buildDependency.add(tasks);
+        return this;
+    }
+
+    public Set<Object> getBuiltBy() {
+        return buildDependency.getValues();
+    }
+
+    public ConfigurableFileTree setBuiltBy(Iterable<?> tasks) {
+        buildDependency.setValues(tasks);
+        return this;
+    }
+
+    @Override
+    public TaskDependency getBuildDependencies() {
+        return buildDependency;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/DefaultFileCollectionResolveContext.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/DefaultFileCollectionResolveContext.java
new file mode 100644
index 0000000..861fe29
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/DefaultFileCollectionResolveContext.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections;
+
+import groovy.lang.Closure;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.file.FileTree;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.file.IdentityFileResolver;
+import org.gradle.api.tasks.TaskDependency;
+import org.gradle.util.GUtil;
+import org.gradle.util.UncheckedException;
+
+import java.io.File;
+import java.util.*;
+import java.util.concurrent.Callable;
+
+public class DefaultFileCollectionResolveContext implements ResolvableFileCollectionResolveContext {
+    private final FileResolver fileResolver;
+    private final List<Object> queue = new LinkedList<Object>();
+    private List<Object> addTo = queue;
+    private final Converter<? extends FileCollection> fileCollectionConverter;
+    private final Converter<? extends FileTree> fileTreeConverter;
+
+    public DefaultFileCollectionResolveContext() {
+        this(new IdentityFileResolver());
+    }
+
+    public DefaultFileCollectionResolveContext(FileResolver fileResolver) {
+        this.fileResolver = fileResolver;
+        fileCollectionConverter = new FileCollectionConverter();
+        fileTreeConverter = new FileTreeConverter();
+    }
+
+    protected DefaultFileCollectionResolveContext(FileResolver fileResolver, Converter<? extends FileCollection> fileCollectionConverter, Converter<? extends FileTree> fileTreeConverter) {
+        this.fileResolver = fileResolver;
+        this.fileCollectionConverter = fileCollectionConverter;
+        this.fileTreeConverter = fileTreeConverter;
+    }
+
+    public DefaultFileCollectionResolveContext add(Object element) {
+        addTo.add(element);
+        return this;
+    }
+
+    public DefaultFileCollectionResolveContext push(FileResolver fileResolver) {
+        DefaultFileCollectionResolveContext nestedContext = new DefaultFileCollectionResolveContext(fileResolver, fileCollectionConverter, fileTreeConverter);
+        add(nestedContext);
+        return nestedContext;
+    }
+
+    public ResolvableFileCollectionResolveContext newContext() {
+        return new DefaultFileCollectionResolveContext(fileResolver, fileCollectionConverter, fileTreeConverter);
+    }
+
+    /**
+     * Resolves the contents of this context as a list of atomic {@link FileTree} instances.
+     */
+    public List<FileTree> resolveAsFileTrees() {
+        return doResolve(fileTreeConverter);
+    }
+
+    /**
+     * Resolves the contents of this context as a list of atomic {@link FileCollection} instances.
+     */
+    public List<FileCollection> resolveAsFileCollections() {
+        return doResolve(fileCollectionConverter);
+    }
+
+    /**
+     * Resolves the contents of this context as a list of atomic {@link MinimalFileCollection} instances.
+     */
+    public List<MinimalFileCollection> resolveAsMinimalFileCollections() {
+        return doResolve(new MinimalFileCollectionConverter());
+    }
+
+    private <T> List<T> doResolve(Converter<? extends T> converter) {
+        List<T> result = new ArrayList<T>();
+        while (!queue.isEmpty()) {
+            Object element = queue.remove(0);
+            if (element instanceof DefaultFileCollectionResolveContext) {
+                DefaultFileCollectionResolveContext nestedContext = (DefaultFileCollectionResolveContext) element;
+                converter.convertInto(nestedContext, result, fileResolver);
+            } else if (element instanceof FileCollectionContainer) {
+                FileCollectionContainer fileCollection = (FileCollectionContainer) element;
+                resolveNested(fileCollection);
+            } else if (element instanceof FileCollection || element instanceof MinimalFileCollection) {
+                converter.convertInto(element, result, fileResolver);
+            } else if (element instanceof Closure) {
+                Closure closure = (Closure) element;
+                Object closureResult = closure.call();
+                if (closureResult != null) {
+                    queue.add(0, closureResult);
+                }
+            } else if (element instanceof Callable) {
+                Callable callable = (Callable) element;
+                Object callableResult;
+                try {
+                    callableResult = callable.call();
+                } catch (Exception e) {
+                    throw UncheckedException.asUncheckedException(e);
+                }
+                if (callableResult != null) {
+                    queue.add(0, callableResult);
+                }
+            } else if (element instanceof Iterable) {
+                Iterable<?> iterable = (Iterable) element;
+                GUtil.addToCollection(queue.subList(0, 0), iterable);
+            } else if (element instanceof Object[]) {
+                Object[] array = (Object[]) element;
+                GUtil.addToCollection(queue.subList(0, 0), Arrays.asList(array));
+            } else {
+                converter.convertInto(element, result, fileResolver);
+            }
+        }
+        return result;
+    }
+
+    private void resolveNested(FileCollectionContainer fileCollection) {
+        addTo = queue.subList(0, 0);
+        try {
+            fileCollection.resolve(this);
+        } finally {
+            addTo = queue;
+        }
+    }
+
+    protected interface Converter<T> {
+        void convertInto(Object element, Collection<? super T> result, FileResolver resolver);
+    }
+
+    private static class FileCollectionConverter implements Converter<FileCollection> {
+        public void convertInto(Object element, Collection<? super FileCollection> result, FileResolver fileResolver) {
+            if (element instanceof DefaultFileCollectionResolveContext) {
+                DefaultFileCollectionResolveContext nestedContext = (DefaultFileCollectionResolveContext) element;
+                result.addAll(nestedContext.resolveAsFileCollections());
+            } else if (element instanceof FileCollection) {
+                FileCollection fileCollection = (FileCollection) element;
+                result.add(fileCollection);
+            } else if (element instanceof MinimalFileTree) {
+                MinimalFileTree fileTree = (MinimalFileTree) element;
+                result.add(new FileTreeAdapter(fileTree));
+            } else if (element instanceof MinimalFileSet) {
+                MinimalFileSet fileSet = (MinimalFileSet) element;
+                result.add(new FileCollectionAdapter(fileSet));
+            } else if (element instanceof MinimalFileCollection) {
+                throw new UnsupportedOperationException(String.format("Cannot convert instance of %s to FileTree", element.getClass().getSimpleName()));
+            } else if (element instanceof TaskDependency) {
+                // Ignore
+                return;
+            } else {
+                result.add(new FileCollectionAdapter(new ListBackedFileSet(fileResolver.resolve(element))));
+            }
+        }
+    }
+
+    private static class FileTreeConverter implements Converter<FileTree> {
+        public void convertInto(Object element, Collection<? super FileTree> result, FileResolver fileResolver) {
+            if (element instanceof DefaultFileCollectionResolveContext) {
+                DefaultFileCollectionResolveContext nestedContext = (DefaultFileCollectionResolveContext) element;
+                result.addAll(nestedContext.resolveAsFileTrees());
+            } else if (element instanceof FileTree) {
+                FileTree fileTree = (FileTree) element;
+                result.add(fileTree);
+            } else if (element instanceof MinimalFileTree) {
+                MinimalFileTree fileTree = (MinimalFileTree) element;
+                result.add(new FileTreeAdapter(fileTree));
+            } else if (element instanceof MinimalFileSet) {
+                MinimalFileSet fileSet = (MinimalFileSet) element;
+                for (File file : fileSet.getFiles()) {
+                    convertFileToFileTree(file, result);
+                }
+            } else if (element instanceof FileCollection || element instanceof MinimalFileCollection) {
+                throw new UnsupportedOperationException(String.format("Cannot convert instance of %s to FileTree", element.getClass().getSimpleName()));
+            } else if (element instanceof TaskDependency) {
+                // Ignore
+                return;
+            } else {
+                convertFileToFileTree(fileResolver.resolve(element), result);
+            }
+        }
+
+        private void convertFileToFileTree(File file, Collection<? super FileTree> result) {
+            if (file.isDirectory()) {
+                result.add(new FileTreeAdapter(new DirectoryFileTree(file)));
+            } else if (file.isFile()) {
+                result.add(new FileTreeAdapter(new SingletonFileTree(file)));
+            }
+        }
+    }
+
+    private static class MinimalFileCollectionConverter implements Converter<MinimalFileCollection> {
+        public void convertInto(Object element, Collection<? super MinimalFileCollection> result, FileResolver resolver) {
+            if (element instanceof DefaultFileCollectionResolveContext) {
+                DefaultFileCollectionResolveContext nestedContext = (DefaultFileCollectionResolveContext) element;
+                result.addAll(nestedContext.resolveAsMinimalFileCollections());
+            } else if (element instanceof MinimalFileCollection) {
+                MinimalFileCollection collection = (MinimalFileCollection) element;
+                result.add(collection);
+            } else if (element instanceof FileCollection) {
+                throw new UnsupportedOperationException(String.format("Cannot convert instance of %s to MinimalFileCollection", element.getClass().getSimpleName()));
+            } else if (element instanceof TaskDependency) {
+                // Ignore
+                return;
+            } else {
+                result.add(new ListBackedFileSet(resolver.resolve(element)));
+            }
+        }
+    }
+}
+
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/DirectoryFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/DirectoryFileTree.java
new file mode 100644
index 0000000..7818e6c
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/DirectoryFileTree.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.file.*;
+import org.gradle.api.internal.file.DefaultFileTreeElement;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.api.specs.Spec;
+import org.gradle.api.tasks.util.PatternFilterable;
+import org.gradle.api.tasks.util.PatternSet;
+import org.gradle.util.GFileUtils;
+import org.gradle.util.GUtil;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.regex.Pattern;
+
+/**
+ * Directory walker supporting {@link Spec}s for includes and excludes.
+ * The file system is traversed breadth first - all files in a directory will be
+ * visited before any child directory is visited.
+ *
+ * A file or directory will only be visited if it matches all includes and no
+ * excludes.
+ *
+ * @author Steve Appling
+ */
+public class DirectoryFileTree implements MinimalFileTree, PatternFilterableFileTree, RandomAccessFileCollection, LocalFileTree, DirectoryTree {
+    private static final Logger LOGGER = Logging.getLogger(DirectoryFileTree.class);
+
+    private final File dir;
+    private PatternSet patternSet;
+    private boolean depthFirst;
+
+    public DirectoryFileTree(File dir) {
+        this(dir, new PatternSet());
+    }
+
+    public DirectoryFileTree(File dir, PatternSet patternSet) {
+        this.patternSet = patternSet;
+        this.dir = GFileUtils.canonicalise(dir);
+    }
+
+    public String getDisplayName() {
+        String includes = patternSet.getIncludes().isEmpty() ? "" : String.format(" include %s", GUtil.toString(patternSet.getIncludes()));
+        String excludes = patternSet.getExcludes().isEmpty() ? "" : String.format(" exclude %s", GUtil.toString(patternSet.getExcludes()));
+        return String.format("directory '%s'%s%s", dir, includes, excludes);
+    }
+
+    @Override
+    public String toString() {
+        return getDisplayName();
+    }
+
+    public PatternSet getPatterns() {
+        return patternSet;
+    }
+
+    public File getDir() {
+        return dir;
+    }
+
+    public Collection<DirectoryFileTree> getLocalContents() {
+        return Collections.singletonList(this);
+    }
+
+    public DirectoryFileTree filter(PatternFilterable patterns) {
+        PatternSet patternSet = this.patternSet.intersect();
+        patternSet.copyFrom(patterns);
+        return new DirectoryFileTree(dir, patternSet);
+    }
+
+    public boolean contains(File file) {
+        String prefix = dir.getAbsolutePath() + File.separator;
+        if (!file.getAbsolutePath().startsWith(prefix)) {
+            return false;
+        }
+        if (!file.isFile()) {
+            return false;
+        }
+        RelativePath path = new RelativePath(true, file.getAbsolutePath().substring(prefix.length()).split(
+                Pattern.quote(File.separator)));
+        return patternSet.getAsSpec().isSatisfiedBy(new DefaultFileTreeElement(file, path));
+    }
+
+    /**
+     * Process the specified file or directory.  Note that the startFile parameter
+     * may be either a directory or a file.  If it is a directory, then it's contents
+     * (but not the directory itself) will be checked with isAllowed and notified to
+     * the listener.  If it is a file, the file will be checked and notified.
+     */
+    public void visit(FileVisitor visitor) {
+        AtomicBoolean stopFlag = new AtomicBoolean();
+        Spec<FileTreeElement> spec = patternSet.getAsSpec();
+        if (dir.exists()) {
+            if (dir.isFile()) {
+                processSingleFile(dir, visitor, spec, stopFlag);
+            } else {
+                walkDir(dir, new RelativePath(false), visitor, spec, stopFlag);
+            }
+        } else {
+            LOGGER.info("file or directory '" + dir + "', not found");
+        }
+    }
+
+    private void processSingleFile(File file, FileVisitor visitor, Spec<FileTreeElement> spec, AtomicBoolean stopFlag) {
+        RelativePath path = new RelativePath(true, file.getName());
+        FileVisitDetailsImpl details = new FileVisitDetailsImpl(file, path, stopFlag);
+        if (isAllowed(details, spec)) {
+            visitor.visitFile(details);
+        }
+    }
+
+    private void walkDir(File file, RelativePath path, FileVisitor visitor, Spec<FileTreeElement> spec, AtomicBoolean stopFlag) {
+        File[] children = file.listFiles();
+        if (children == null) {
+            if (file.isDirectory() && !file.canRead()) {
+                throw new GradleException(String.format("Could not list contents of directory '%s' as it is not readable.", file));
+            }
+            // else, might be a link which points to nothing, or has been removed while we're visiting, or ...
+            throw new GradleException(String.format("Could not list contents of '%s'.", file));
+        }
+        List<FileVisitDetailsImpl> dirs = new ArrayList<FileVisitDetailsImpl>();
+        for (int i = 0; !stopFlag.get() && i < children.length; i++) {
+            File child = children[i];
+            boolean isFile = child.isFile();
+            RelativePath childPath = path.append(isFile, child.getName());
+            FileVisitDetailsImpl details = new FileVisitDetailsImpl(child, childPath, stopFlag);
+            if (isAllowed(details, spec)) {
+                if (isFile) {
+                    visitor.visitFile(details);
+                } else {
+                    dirs.add(details);
+                }
+            }
+        }
+
+        // now handle dirs
+        for (int i = 0; !stopFlag.get() && i < dirs.size(); i++) {
+            FileVisitDetailsImpl dir = dirs.get(i);
+            if (depthFirst) {
+                walkDir(dir.getFile(), dir.getRelativePath(), visitor, spec, stopFlag);
+                visitor.visitDir(dir);
+            } else {
+                visitor.visitDir(dir);
+                walkDir(dir.getFile(), dir.getRelativePath(), visitor, spec, stopFlag);
+            }
+        }
+    }
+
+    boolean isAllowed(FileTreeElement element, Spec<FileTreeElement> spec) {
+        return spec.isSatisfiedBy(element);
+    }
+
+    public DirectoryFileTree depthFirst() {
+        depthFirst = true;
+        return this;
+    }
+
+    private static class FileVisitDetailsImpl extends DefaultFileTreeElement implements FileVisitDetails {
+        private final AtomicBoolean stop;
+
+        private FileVisitDetailsImpl(File file, RelativePath relativePath, AtomicBoolean stop) {
+            super(file, relativePath);
+            this.stop = stop;
+        }
+
+        public void stopVisiting() {
+            stop.set(true);
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/EmptyFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/EmptyFileTree.java
new file mode 100644
index 0000000..d2beb27
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/EmptyFileTree.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections;
+
+import org.gradle.api.Buildable;
+import org.gradle.api.file.FileVisitor;
+import org.gradle.api.tasks.TaskDependency;
+
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * An empty file collection which is used to mix in some build dependencies.
+ */
+public class EmptyFileTree implements MinimalFileTree, Buildable, LocalFileTree {
+    private final TaskDependency buildDependencies;
+
+    public EmptyFileTree(TaskDependency buildDependencies) {
+        this.buildDependencies = buildDependencies;
+    }
+
+    public TaskDependency getBuildDependencies() {
+        return buildDependencies;
+    }
+
+    public String getDisplayName() {
+        return "dependencies mix-in file collection";
+    }
+
+    public Collection<DirectoryFileTree> getLocalContents() {
+        return Collections.emptyList();
+    }
+
+    public void visit(FileVisitor visitor) {
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/FileCollectionAdapter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/FileCollectionAdapter.java
new file mode 100644
index 0000000..18e1e2e
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/FileCollectionAdapter.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections;
+
+import org.gradle.api.Buildable;
+import org.gradle.api.internal.file.AbstractFileCollection;
+import org.gradle.api.tasks.TaskDependency;
+
+import java.io.File;
+import java.util.Set;
+
+/**
+ * Adapts a {@link MinimalFileSet} into a full {@link org.gradle.api.file.FileCollection}.
+ */
+public class FileCollectionAdapter extends AbstractFileCollection implements FileCollectionContainer {
+    private final MinimalFileSet fileCollection;
+
+    public FileCollectionAdapter(MinimalFileSet fileSet) {
+        this.fileCollection = fileSet;
+    }
+
+    @Override
+    public String getDisplayName() {
+        return fileCollection.getDisplayName();
+    }
+
+    public void resolve(FileCollectionResolveContext context) {
+        context.add(fileCollection);
+    }
+
+    public Set<File> getFiles() {
+        return fileCollection.getFiles();
+    }
+
+    @Override
+    public TaskDependency getBuildDependencies() {
+        if (fileCollection instanceof Buildable) {
+            Buildable buildable = (Buildable) fileCollection;
+            return buildable.getBuildDependencies();
+        }
+        return super.getBuildDependencies();
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/FileCollectionContainer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/FileCollectionContainer.java
new file mode 100644
index 0000000..82d17f4
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/FileCollectionContainer.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections;
+
+/**
+ * A file collection made up of other file collections. Generally, this is either a simple collection, or may be a factory for file collections.
+ */
+public interface FileCollectionContainer {
+    String getDisplayName();
+
+    /**
+     * Adds the contents of this container to the given context.
+     */
+    void resolve(FileCollectionResolveContext context);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/FileCollectionResolveContext.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/FileCollectionResolveContext.java
new file mode 100644
index 0000000..7fd43a3
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/FileCollectionResolveContext.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections;
+
+import org.gradle.api.internal.file.FileResolver;
+
+public interface FileCollectionResolveContext {
+    /**
+     * Adds the given element to be resolved. Handles the following types:
+     *
+     * <ul>
+     *     <li>{@link Iterable} - elements are recursively resolved.
+     *     <li>{@link groovy.lang.Closure} - return value is recursively resolved, if not null.
+     *     <li>{@link java.util.concurrent.Callable} - return value is recursively resolved, if not null.
+     *     <li>{@link org.gradle.api.file.FileCollection} - resolved as is.
+     *     <li>{@link MinimalFileSet} - wrapped as a {@link org.gradle.api.file.FileCollection}.
+     *     <li>{@link MinimalFileTree} - wrapped as a {@link org.gradle.api.file.FileTree}.
+     *     <li>{@link FileCollectionContainer} - recursively resolved.
+     *     <li>{@link org.gradle.api.tasks.TaskDependency} - resolved to an empty {@link org.gradle.api.file.FileCollection} which is builtBy the given dependency.
+     *     <li>Everything else - resolved to a File and wrapped in a singleton {@link org.gradle.api.file.FileCollection}.
+     * </ul>
+     *
+     * Generally, the result of resolution is a composite {@link org.gradle.api.file.FileCollection} which contains the union of all files and dependencies add to this context.
+     *
+     * @param element The element to add.
+     * @return this
+     */
+    FileCollectionResolveContext add(Object element);
+
+    /**
+     * Adds a nested context which resolves elements using the given resolvers. Any element added to the returned context will be added to this context. Those elements
+     * which need to be resolved using a file resolver will use the provided resolver, instead of the default used by this context.
+     */
+    FileCollectionResolveContext push(FileResolver fileResolver);
+
+    /**
+     * Creates a new context which can be used to resolve element. Elements added to the returned context will not be added to this context. Instead, the caller should use
+     * one of {@link ResolvableFileCollectionResolveContext#resolveAsFileCollections()} or {@link ResolvableFileCollectionResolveContext#resolveAsFileTrees()}.
+     */
+    ResolvableFileCollectionResolveContext newContext();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/FileSystemMirroringFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/FileSystemMirroringFileTree.java
new file mode 100644
index 0000000..bf16227
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/FileSystemMirroringFileTree.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections;
+
+/**
+ * A file tree which maintains a local copy of itself on the filesystem.
+ */
+public interface FileSystemMirroringFileTree extends MinimalFileTree {
+    /**
+     * Returns the directory tree that will contain the copy of this file tree, after all elements of this tree have been visited. It is the caller's responsibility to visit the
+     * elements of this tree before using the returned directory tree.
+     */
+    DirectoryFileTree getMirror();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/FileTreeAdapter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/FileTreeAdapter.java
new file mode 100644
index 0000000..cd000bd
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/FileTreeAdapter.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections;
+
+import org.gradle.api.Buildable;
+import org.gradle.api.file.FileTree;
+import org.gradle.api.file.FileVisitor;
+import org.gradle.api.internal.file.AbstractFileTree;
+import org.gradle.api.tasks.TaskDependency;
+import org.gradle.api.tasks.util.PatternFilterable;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * Adapts a {@link MinimalFileTree} into a full {@link FileTree} implementation.
+ */
+public class FileTreeAdapter extends AbstractFileTree implements FileCollectionContainer {
+    private final MinimalFileTree tree;
+
+    public FileTreeAdapter(MinimalFileTree tree) {
+        this.tree = tree;
+    }
+
+    public MinimalFileTree getTree() {
+        return tree;
+    }
+
+    @Override
+    public String getDisplayName() {
+        return tree.getDisplayName();
+    }
+
+    public void resolve(FileCollectionResolveContext context) {
+        context.add(tree);
+    }
+
+    @Override
+    protected Collection<DirectoryFileTree> getAsFileTrees() {
+        if (tree instanceof FileSystemMirroringFileTree) {
+            FileSystemMirroringFileTree mirroringTree = (FileSystemMirroringFileTree) tree;
+            if (visitAll()) {
+                return Collections.singletonList(mirroringTree.getMirror());
+            } else {
+                return Collections.emptyList();
+            }
+        } else if (tree instanceof LocalFileTree) {
+            LocalFileTree fileTree = (LocalFileTree) tree;
+            return fileTree.getLocalContents();
+        }
+        throw new UnsupportedOperationException(String.format("Cannot convert %s to local file system directories.", tree));
+    }
+
+    @Override
+    public TaskDependency getBuildDependencies() {
+        if (tree instanceof Buildable) {
+            Buildable buildable = (Buildable) tree;
+            return buildable.getBuildDependencies();
+        }
+        return super.getBuildDependencies();
+    }
+
+    @Override
+    public boolean contains(File file) {
+        if (tree instanceof RandomAccessFileCollection) {
+            RandomAccessFileCollection randomAccess = (RandomAccessFileCollection) tree;
+            return randomAccess.contains(file);
+        }
+        return super.contains(file);
+    }
+
+    @Override
+    public FileTree matching(PatternFilterable patterns) {
+        if (tree instanceof PatternFilterableFileTree) {
+            PatternFilterableFileTree filterableTree = (PatternFilterableFileTree) tree;
+            return new FileTreeAdapter(filterableTree.filter(patterns));
+        }
+        return super.matching(patterns);
+    }
+
+    public FileTree visit(FileVisitor visitor) {
+        tree.visit(visitor);
+        return this;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/ListBackedFileSet.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/ListBackedFileSet.java
new file mode 100644
index 0000000..f230f74
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/ListBackedFileSet.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections;
+
+import org.gradle.util.GUtil;
+
+import java.io.File;
+import java.util.*;
+
+/**
+ * Adapts a java util collection into a file set.
+ */
+public class ListBackedFileSet implements MinimalFileSet {
+    private final Set<File> files;
+
+    public ListBackedFileSet(File... files) {
+        this(Arrays.asList(files));
+    }
+
+    public ListBackedFileSet(Collection<File> files) {
+        this.files = new LinkedHashSet<File>(files);
+    }
+
+    public String getDisplayName() {
+        switch (files.size()) {
+            case 0:
+                return "empty file collection";
+            case 1:
+                return String.format("file '%s'", files.iterator().next());
+            default:
+                return String.format("files %s", GUtil.toString(files));
+        }
+    }
+
+    public Set<File> getFiles() {
+        return files;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/LocalFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/LocalFileTree.java
new file mode 100644
index 0000000..ecdba79
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/LocalFileTree.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections;
+
+import java.util.Collection;
+
+/**
+ * A file tree whose contents are contained in zero or more local directories.
+ */
+public interface LocalFileTree extends MinimalFileTree {
+    Collection<DirectoryFileTree> getLocalContents();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/MapFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/MapFileTree.java
new file mode 100644
index 0000000..9224e65
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/MapFileTree.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections;
+
+import groovy.lang.Closure;
+import org.gradle.api.file.FileVisitDetails;
+import org.gradle.api.file.FileVisitor;
+import org.gradle.api.file.RelativePath;
+import org.gradle.api.internal.file.AbstractFileTreeElement;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * A {@link MinimalFileTree} which is composed using a mapping from relative path to file source.
+ */
+public class MapFileTree implements MinimalFileTree, FileSystemMirroringFileTree {
+    private final Map<RelativePath, Closure> elements = new LinkedHashMap<RelativePath, Closure>();
+    private final File tmpDir;
+
+    public MapFileTree(File tmpDir) {
+        this.tmpDir = tmpDir;
+    }
+
+    public String getDisplayName() {
+        return "file tree";
+    }
+
+    public DirectoryFileTree getMirror() {
+        return new DirectoryFileTree(tmpDir);
+    }
+
+    public void visit(FileVisitor visitor) {
+        AtomicBoolean stopFlag = new AtomicBoolean();
+        Visit visit = new Visit(visitor, stopFlag);
+        for (Map.Entry<RelativePath, Closure> entry : elements.entrySet()) {
+            if (stopFlag.get()) {
+                break;
+            }
+            RelativePath path = entry.getKey();
+            Closure generator = entry.getValue();
+            visit.visit(path, generator);
+        }
+    }
+
+    /**
+     * Adds an element to this tree. The given closure is passed an OutputStream which it can use to write the content
+     * of the element to.
+     */
+    public void add(String path, Closure contentClosure) {
+        elements.put(RelativePath.parse(true, path), contentClosure);
+    }
+
+    private class Visit {
+        private final Set<RelativePath> visitedDirs = new LinkedHashSet<RelativePath>();
+        private final FileVisitor visitor;
+        private final AtomicBoolean stopFlag;
+
+        public Visit(FileVisitor visitor, AtomicBoolean stopFlag) {
+            this.visitor = visitor;
+            this.stopFlag = stopFlag;
+        }
+
+        private void visitDirs(RelativePath path, FileVisitor visitor) {
+            if (path == null || path.getParent() == null || !visitedDirs.add(path)) {
+                return;
+            }
+
+            visitDirs(path.getParent(), visitor);
+            visitor.visitDir(new FileVisitDetailsImpl(path, null, stopFlag));
+        }
+
+        public void visit(RelativePath path, Closure generator) {
+            visitDirs(path.getParent(), visitor);
+            visitor.visitFile(new FileVisitDetailsImpl(path, generator, stopFlag));
+        }
+    }
+
+    private class FileVisitDetailsImpl extends AbstractFileTreeElement implements FileVisitDetails {
+        private final RelativePath path;
+        private final Closure generator;
+        private final long lastModified;
+        private final AtomicBoolean stopFlag;
+        private File file;
+
+        public FileVisitDetailsImpl(RelativePath path, Closure generator, AtomicBoolean stopFlag) {
+            this.path = path;
+            this.generator = generator;
+            this.stopFlag = stopFlag;
+            // round to nearest second
+            lastModified = System.currentTimeMillis() / 1000 * 1000;
+        }
+
+        public String getDisplayName() {
+            return path.toString();
+        }
+
+        public void stopVisiting() {
+            stopFlag.set(true);
+        }
+
+        public File getFile() {
+            if (file == null) {
+                file = path.getFile(tmpDir);
+                copyTo(file);
+            }
+            return file;
+        }
+
+        public boolean isDirectory() {
+            return !path.isFile();
+        }
+
+        public long getLastModified() {
+            return lastModified;
+        }
+
+        public long getSize() {
+            return getFile().length();
+        }
+
+        public void copyTo(OutputStream outstr) {
+            generator.call(outstr);
+        }
+
+        public InputStream open() {
+            throw new UnsupportedOperationException();
+        }
+
+        public RelativePath getRelativePath() {
+            return path;
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/MinimalFileCollection.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/MinimalFileCollection.java
new file mode 100644
index 0000000..4c5bcd2
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/MinimalFileCollection.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections;
+
+/**
+ * A minimal file collection. An implementation can optionally also implement the following interfaces:
+ *
+ * <ul>
+ * <li>{@link org.gradle.api.Buildable}</li>
+ * <li>{@link RandomAccessFileCollection}</li>
+ * </ul>
+ */
+public interface MinimalFileCollection {
+    String getDisplayName();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/MinimalFileSet.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/MinimalFileSet.java
new file mode 100644
index 0000000..4933d3e
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/MinimalFileSet.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections;
+
+import java.io.File;
+import java.util.Set;
+
+/**
+ * A minimal file set.
+ */
+public interface MinimalFileSet extends MinimalFileCollection {
+    Set<File> getFiles();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/MinimalFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/MinimalFileTree.java
new file mode 100644
index 0000000..6f0e38c
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/MinimalFileTree.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections;
+
+import org.gradle.api.file.FileVisitor;
+
+/**
+ * A minimal file tree implementation. An implementation can optionally also implement the following interfaces:
+ *
+ * <ul>
+ *  <li>{@link FileSystemMirroringFileTree}</li>
+ *  <li>{@link LocalFileTree}</li>
+ *  <li>{@link PatternFilterableFileTree}</li>
+ * </ul>
+ */
+public interface MinimalFileTree extends MinimalFileCollection {
+    /**
+     * Visits the elements of this tree, in breadth-wise order.
+     */
+    void visit(FileVisitor visitor);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/PatternFilterableFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/PatternFilterableFileTree.java
new file mode 100644
index 0000000..9400740
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/PatternFilterableFileTree.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections;
+
+import org.gradle.api.tasks.util.PatternFilterable;
+
+/**
+ * A file tree which can provide an efficient implementation for filtering using patterns.
+ */
+public interface PatternFilterableFileTree extends MinimalFileTree {
+    MinimalFileTree filter(PatternFilterable patterns);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/RandomAccessFileCollection.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/RandomAccessFileCollection.java
new file mode 100644
index 0000000..0fcfa9c
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/RandomAccessFileCollection.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections;
+
+import java.io.File;
+
+/**
+ * A file collection which can provide an efficient implementation to determine if it contains a given file.
+ */
+public interface RandomAccessFileCollection {
+    boolean contains(File file);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/ResolvableFileCollectionResolveContext.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/ResolvableFileCollectionResolveContext.java
new file mode 100644
index 0000000..ca62771
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/ResolvableFileCollectionResolveContext.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.file.FileTree;
+
+import java.util.List;
+
+public interface ResolvableFileCollectionResolveContext extends FileCollectionResolveContext {
+    /**
+     * Resolves the contents of this context as a sequence of atomic file collections.
+     */
+    List<FileCollection> resolveAsFileCollections();
+
+    /**
+     * Resolves the contents of this context as a sequence of atomic file trees.
+     */
+    List<FileTree> resolveAsFileTrees();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/SimpleFileCollection.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/SimpleFileCollection.java
new file mode 100644
index 0000000..fb87dda
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/SimpleFileCollection.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.file.collections;
+
+import java.io.File;
+import java.util.Collection;
+
+public class SimpleFileCollection extends FileCollectionAdapter {
+    public SimpleFileCollection(File... files) {
+        super(new ListBackedFileSet(files));
+    }
+
+    public SimpleFileCollection(Collection<File> files) {
+        super(new ListBackedFileSet(files));
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/SingletonFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/SingletonFileTree.java
new file mode 100644
index 0000000..348723c
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/SingletonFileTree.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections;
+
+import org.gradle.api.file.FileVisitDetails;
+import org.gradle.api.file.FileVisitor;
+import org.gradle.api.file.RelativePath;
+import org.gradle.api.internal.file.DefaultFileTreeElement;
+
+import java.io.File;
+
+/**
+ * A file tree with a single file entry.
+ */
+public class SingletonFileTree implements MinimalFileTree {
+    private final File file;
+
+    public SingletonFileTree(File file) {
+        this.file = file;
+    }
+
+    public String getDisplayName() {
+        return String.format("file '%s'", file);
+    }
+
+    public void visit(FileVisitor visitor) {
+        visitor.visitFile(new FileVisitDetailsImpl());
+    }
+
+    private class FileVisitDetailsImpl extends DefaultFileTreeElement implements FileVisitDetails {
+        private FileVisitDetailsImpl() {
+            super(file, new RelativePath(true, file.getName()));
+        }
+
+        public void stopVisiting() {
+        }
+    }
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopyActionImpl.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopyActionImpl.java
index 9c09691..01730f8 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopyActionImpl.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopyActionImpl.java
@@ -1,236 +1,244 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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 groovy.lang.Closure;
-import org.gradle.api.Action;
-import org.gradle.api.file.*;
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.specs.Spec;
-
-import java.io.FilterReader;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Pattern;
-
-/**
- * @author Steve Appling
- */
-public class CopyActionImpl implements CopyAction, CopySpecSource {
-    private final CopySpecVisitor visitor;
-    private final CopySpecImpl root;
-    private final CopySpecImpl mainContent;
-    private final FileResolver resolver;
-
-    public CopyActionImpl(FileResolver resolver, CopySpecVisitor visitor) {
-        this.resolver = resolver;
-        root = new CopySpecImpl(resolver);
-        mainContent = root.addChild();
-        this.visitor = new MappingCopySpecVisitor(new NormalizingCopySpecVisitor(visitor));
-    }
-
-    public FileResolver getResolver() {
-        return resolver;
-    }
-
-    public CopySpecImpl getRootSpec() {
-        return root;
-    }
-
-    public CopySpecImpl getMainSpec() {
-        return mainContent;
-    }
-
-    public void execute() {
-        visitor.startVisit(this);
-        for (ReadableCopySpec spec : root.getAllSpecs()) {
-            visitor.visitSpec(spec);
-            spec.getSource().visit(visitor);
-        }
-        visitor.endVisit();
-    }
-
-    public boolean getDidWork() {
-        return visitor.getDidWork();
-    }
-
-    public FileTree getAllSource() {
-        List<FileTree> sources = new ArrayList<FileTree>();
-        for (ReadableCopySpec spec : root.getAllSpecs()) {
-            FileTree source = spec.getSource();
-            sources.add(source);
-        }
-        return resolver.resolveFilesAsTree(sources);
-    }
-
-    public boolean hasSource() {
-        return root.hasSource();
-    }
-
-    public CopySpec eachFile(Action<? super FileCopyDetails> action) {
-        mainContent.eachFile(action);
-        return this;
-    }
-
-    public CopySpec eachFile(Closure closure) {
-        mainContent.eachFile(closure);
-        return this;
-    }
-
-    public CopySpec exclude(Iterable<String> excludes) {
-        mainContent.exclude(excludes);
-        return this;
-    }
-
-    public CopySpec exclude(String... excludes) {
-        mainContent.exclude(excludes);
-        return this;
-    }
-
-    public CopySpec exclude(Closure excludeSpec) {
-        mainContent.exclude(excludeSpec);
-        return this;
-    }
-
-    public CopySpec exclude(Spec<FileTreeElement> excludeSpec) {
-        mainContent.exclude(excludeSpec);
-        return this;
-    }
-
-    public CopySpec expand(Map<String, ?> properties) {
-        mainContent.expand(properties);
-        return this;
-    }
-
-    public CopySpec filter(Closure closure) {
-        mainContent.filter(closure);
-        return this;
-    }
-
-    public CopySpec filter(Class<? extends FilterReader> filterType) {
-        mainContent.filter(filterType);
-        return this;
-    }
-
-    public CopySpec filter(Map<String, ?> properties, Class<? extends FilterReader> filterType) {
-        mainContent.filter(properties, filterType);
-        return this;
-    }
-
-    public CopySpec from(Object sourcePath, Closure c) {
-        return mainContent.from(sourcePath, c);
-    }
-
-    public CopySpec from(Object... sourcePaths) {
-        mainContent.from(sourcePaths);
-        return this;
-    }
-
-    public Set<String> getExcludes() {
-        return mainContent.getExcludes();
-    }
-
-    public Set<String> getIncludes() {
-        return mainContent.getIncludes();
-    }
-
-    public CopySpec include(Iterable<String> includes) {
-        mainContent.include(includes);
-        return this;
-    }
-
-    public CopySpec include(String... includes) {
-        mainContent.include(includes);
-        return this;
-    }
-
-    public CopySpec include(Closure includeSpec) {
-        mainContent.include(includeSpec);
-        return this;
-    }
-
-    public CopySpec include(Spec<FileTreeElement> includeSpec) {
-        mainContent.include(includeSpec);
-        return this;
-    }
-
-    public CopySpec into(Object destDir) {
-        mainContent.into(destDir);
-        return this;
-    }
-
-    public CopySpec into(Object destPath, Closure configureClosure) {
-        return mainContent.into(destPath, configureClosure);
-    }
-
-    public boolean isCaseSensitive() {
-        return mainContent.isCaseSensitive();
-    }
-
-    public CopySpec rename(Closure closure) {
-        mainContent.rename(closure);
-        return this;
-    }
-
-    public CopySpec rename(Pattern sourceRegEx, String replaceWith) {
-        mainContent.rename(sourceRegEx, replaceWith);
-        return this;
-    }
-
-    public CopySpec rename(String sourceRegEx, String replaceWith) {
-        mainContent.rename(sourceRegEx, replaceWith);
-        return this;
-    }
-
-    public void setCaseSensitive(boolean caseSensitive) {
-        mainContent.setCaseSensitive(caseSensitive);
-    }
-
-    public int getDirMode() {
-        return mainContent.getDirMode();
-    }
-
-    public CopyProcessingSpec setDirMode(int mode) {
-        mainContent.setDirMode(mode);
-        return this;
-    }
-
-    public CopySpec setExcludes(Iterable<String> excludes) {
-        mainContent.setExcludes(excludes);
-        return this;
-    }
-
-    public int getFileMode() {
-        return mainContent.getFileMode();
-    }
-
-    public CopyProcessingSpec setFileMode(int mode) {
-        mainContent.setFileMode(mode);
-        return this;
-    }
-
-    public CopySpec setIncludes(Iterable<String> includes) {
-        mainContent.setIncludes(includes);
-        return this;
-    }
-
-    public CopySpec with(CopySpec... copySpecs) {
-        mainContent.with(copySpecs);
-        return this;
-    }
-}
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 groovy.lang.Closure;
+import org.gradle.api.Action;
+import org.gradle.api.file.*;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.specs.Spec;
+
+import java.io.FilterReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+/**
+ * @author Steve Appling
+ */
+public class CopyActionImpl implements CopyAction, CopySpecSource {
+    private final CopySpecVisitor visitor;
+    private final CopySpecImpl root;
+    private final CopySpecImpl mainContent;
+    private final FileResolver resolver;
+
+    public CopyActionImpl(FileResolver resolver, CopySpecVisitor visitor) {
+        this.resolver = resolver;
+        root = new CopySpecImpl(resolver);
+        mainContent = root.addChild();
+        this.visitor = new MappingCopySpecVisitor(new NormalizingCopySpecVisitor(visitor));
+    }
+
+    public FileResolver getResolver() {
+        return resolver;
+    }
+
+    public CopySpecImpl getRootSpec() {
+        return root;
+    }
+
+    public CopySpecImpl getMainSpec() {
+        return mainContent;
+    }
+
+    public void execute() {
+        visitor.startVisit(this);
+        for (ReadableCopySpec spec : root.getAllSpecs()) {
+            visitor.visitSpec(spec);
+            spec.getSource().visit(visitor);
+        }
+        visitor.endVisit();
+    }
+
+    public boolean getDidWork() {
+        return visitor.getDidWork();
+    }
+
+    public FileTree getAllSource() {
+        List<FileTree> sources = new ArrayList<FileTree>();
+        for (ReadableCopySpec spec : root.getAllSpecs()) {
+            FileTree source = spec.getSource();
+            sources.add(source);
+        }
+        return resolver.resolveFilesAsTree(sources);
+    }
+
+    public boolean hasSource() {
+        return root.hasSource();
+    }
+
+    public CopySpec eachFile(Action<? super FileCopyDetails> action) {
+        mainContent.eachFile(action);
+        return this;
+    }
+
+    public CopySpec eachFile(Closure closure) {
+        mainContent.eachFile(closure);
+        return this;
+    }
+
+    public CopySpec exclude(Iterable<String> excludes) {
+        mainContent.exclude(excludes);
+        return this;
+    }
+
+    public CopySpec exclude(String... excludes) {
+        mainContent.exclude(excludes);
+        return this;
+    }
+
+    public CopySpec exclude(Closure excludeSpec) {
+        mainContent.exclude(excludeSpec);
+        return this;
+    }
+
+    public CopySpec exclude(Spec<FileTreeElement> excludeSpec) {
+        mainContent.exclude(excludeSpec);
+        return this;
+    }
+
+    public CopySpec expand(Map<String, ?> properties) {
+        mainContent.expand(properties);
+        return this;
+    }
+
+    public CopySpec filter(Closure closure) {
+        mainContent.filter(closure);
+        return this;
+    }
+
+    public CopySpec filter(Class<? extends FilterReader> filterType) {
+        mainContent.filter(filterType);
+        return this;
+    }
+
+    public CopySpec filter(Map<String, ?> properties, Class<? extends FilterReader> filterType) {
+        mainContent.filter(properties, filterType);
+        return this;
+    }
+
+    public CopySpec from(Object sourcePath, Closure c) {
+        return mainContent.from(sourcePath, c);
+    }
+
+    public CopySpec from(Object... sourcePaths) {
+        mainContent.from(sourcePaths);
+        return this;
+    }
+
+    public Set<String> getExcludes() {
+        return mainContent.getExcludes();
+    }
+
+    public Set<String> getIncludes() {
+        return mainContent.getIncludes();
+    }
+
+    public CopySpec include(Iterable<String> includes) {
+        mainContent.include(includes);
+        return this;
+    }
+
+    public CopySpec include(String... includes) {
+        mainContent.include(includes);
+        return this;
+    }
+
+    public CopySpec include(Closure includeSpec) {
+        mainContent.include(includeSpec);
+        return this;
+    }
+
+    public CopySpec include(Spec<FileTreeElement> includeSpec) {
+        mainContent.include(includeSpec);
+        return this;
+    }
+
+    public CopySpec into(Object destDir) {
+        mainContent.into(destDir);
+        return this;
+    }
+
+    public CopySpec into(Object destPath, Closure configureClosure) {
+        return mainContent.into(destPath, configureClosure);
+    }
+
+    public boolean isCaseSensitive() {
+        return mainContent.isCaseSensitive();
+    }
+
+    public boolean getIncludeEmptyDirs() {
+        return mainContent.getIncludeEmptyDirs();
+    }
+
+    public void setIncludeEmptyDirs(boolean includeEmptyDirs) {
+        mainContent.setIncludeEmptyDirs(includeEmptyDirs);
+    }
+
+    public CopySpec rename(Closure closure) {
+        mainContent.rename(closure);
+        return this;
+    }
+
+    public CopySpec rename(Pattern sourceRegEx, String replaceWith) {
+        mainContent.rename(sourceRegEx, replaceWith);
+        return this;
+    }
+
+    public CopySpec rename(String sourceRegEx, String replaceWith) {
+        mainContent.rename(sourceRegEx, replaceWith);
+        return this;
+    }
+
+    public void setCaseSensitive(boolean caseSensitive) {
+        mainContent.setCaseSensitive(caseSensitive);
+    }
+
+    public int getDirMode() {
+        return mainContent.getDirMode();
+    }
+
+    public CopyProcessingSpec setDirMode(int mode) {
+        mainContent.setDirMode(mode);
+        return this;
+    }
+
+    public CopySpec setExcludes(Iterable<String> excludes) {
+        mainContent.setExcludes(excludes);
+        return this;
+    }
+
+    public int getFileMode() {
+        return mainContent.getFileMode();
+    }
+
+    public CopyProcessingSpec setFileMode(int mode) {
+        mainContent.setFileMode(mode);
+        return this;
+    }
+
+    public CopySpec setIncludes(Iterable<String> includes) {
+        mainContent.setIncludes(includes);
+        return this;
+    }
+
+    public CopySpec with(CopySpec... copySpecs) {
+        mainContent.with(copySpecs);
+        return this;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecImpl.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecImpl.java
index c0f846d..c95b39c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecImpl.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecImpl.java
@@ -1,440 +1,473 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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 groovy.lang.Closure;
-import org.apache.tools.zip.UnixStat;
-import org.codehaus.groovy.runtime.DefaultGroovyMethods;
-import org.gradle.api.Action;
-import org.gradle.api.file.*;
-import org.gradle.api.internal.ChainingTransformer;
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.specs.Spec;
-import org.gradle.api.tasks.util.PatternSet;
-import org.gradle.util.ConfigureUtil;
-
-import java.io.File;
-import java.io.FilterReader;
-import java.util.*;
-import java.util.regex.Pattern;
-
-/**
- * @author Steve Appling
- */
-public class CopySpecImpl implements CopySpec, ReadableCopySpec {
-    private final FileResolver resolver;
-    private final Set<Object> sourcePaths;
-    private Object destDir;
-    private final PatternSet patternSet;
-    private final List<ReadableCopySpec> childSpecs;
-    private final CopySpecImpl parentSpec;
-    private final List<Action<? super FileCopyDetails>> actions = new ArrayList<Action<? super FileCopyDetails>>();
-    private Integer dirMode;
-    private Integer fileMode;
-    private Boolean caseSensitive;
-
-    private CopySpecImpl(FileResolver resolver, CopySpecImpl parentSpec) {
-        this.parentSpec = parentSpec;
-        this.resolver = resolver;
-        sourcePaths = new LinkedHashSet<Object>();
-        childSpecs = new ArrayList<ReadableCopySpec>();
-        patternSet = new PatternSet();
-    }
-
-    public CopySpecImpl(FileResolver resolver) {
-        this(resolver, null);
-    }
-
-    protected FileResolver getResolver() {
-        return resolver;
-    }
-
-    public CopySpec with(CopySpec... copySpecs) {
-        for (CopySpec copySpec : copySpecs) {
-            ReadableCopySpec readableCopySpec;
-            if (copySpec instanceof CopySpecSource) {
-                CopySpecSource copySpecSource = (CopySpecSource) copySpec;
-                readableCopySpec = copySpecSource.getRootSpec();
-            } else {
-                readableCopySpec = (ReadableCopySpec) copySpec;
-            }
-            childSpecs.add(new WrapperCopySpec(this, readableCopySpec));
-        }
-        return this;
-    }
-
-    public CopySpec from(Object... sourcePaths) {
-        for (Object sourcePath : sourcePaths) {
-            if (sourcePath instanceof CopySpec) {
-                with((CopySpec) sourcePath);
-            } else {
-                this.sourcePaths.add(sourcePath);
-            }
-        }
-        return this;
-    }
-
-    public CopySpec from(Object sourcePath, Closure c) {
-        if (c == null) {
-            from(sourcePath);
-            return this;
-        } else {
-            CopySpecImpl child = addChild();
-            child.from(sourcePath);
-            ConfigureUtil.configure(c, child);
-            return child;
-        }
-    }
-
-    public CopySpecImpl addFirst() {
-        CopySpecImpl child = new CopySpecImpl(resolver, this);
-        childSpecs.add(0, child);
-        return child;
-    }
-
-    public CopySpecImpl addChild() {
-        CopySpecImpl child = new CopySpecImpl(resolver, this);
-        childSpecs.add(child);
-        return child;
-    }
-
-    public Set<Object> getSourcePaths() {
-        return sourcePaths;
-    }
-
-    public FileTree getSource() {
-        return resolver.resolveFilesAsTree(sourcePaths).matching(getPatternSet());
-    }
-
-    public List<ReadableCopySpec> getAllSpecs() {
-        List<ReadableCopySpec> result = new ArrayList<ReadableCopySpec>();
-        result.add(this);
-        for (ReadableCopySpec childSpec : childSpecs) {
-            result.addAll(childSpec.getAllSpecs());
-        }
-        return result;
-    }
-
-    public CopySpecImpl into(Object destDir) {
-        this.destDir = destDir;
-        return this;
-    }
-
-    public CopySpecImpl into(Object destPath, Closure configureClosure) {
-        if (configureClosure == null) {
-            into(destPath);
-            return this;
-        } else {
-            CopySpecImpl child = addChild();
-            child.into(destPath);
-            ConfigureUtil.configure(configureClosure, child);
-            return child;
-        }
-    }
-
-    public RelativePath getDestPath() {
-        RelativePath parentPath;
-        if (parentSpec == null) {
-            parentPath = new RelativePath(false);
-        } else {
-            parentPath = parentSpec.getDestPath();
-        }
-        if (destDir == null) {
-            return parentPath;
-        }
-
-        String path = destDir.toString();
-        if (path.startsWith("/") || path.startsWith(File.separator)) {
-            return RelativePath.parse(false, path);
-        }
-
-        return RelativePath.parse(false, parentPath, path);
-    }
-
-    public PatternSet getPatternSet() {
-        PatternSet patterns = new PatternSet();
-        patterns.setCaseSensitive(isCaseSensitive());
-        patterns.include(getAllIncludes());
-        patterns.includeSpecs(getAllIncludeSpecs());
-        patterns.exclude(getAllExcludes());
-        patterns.excludeSpecs(getAllExcludeSpecs());
-        return patterns;
-    }
-
-    public boolean isCaseSensitive() {
-        if (caseSensitive != null) {
-            return caseSensitive;
-        } else if (parentSpec != null) {
-            return parentSpec.isCaseSensitive();
-        }
-        return true;
-    }
-
-    public void setCaseSensitive(boolean caseSensitive) {
-        this.caseSensitive = caseSensitive;
-    }
-
-    public CopySpec include(String... includes) {
-        patternSet.include(includes);
-        return this;
-    }
-
-    public CopySpec include(Iterable<String> includes) {
-        patternSet.include(includes);
-        return this;
-    }
-
-    public CopySpec include(Spec<FileTreeElement> includeSpec) {
-        patternSet.include(includeSpec);
-        return this;
-    }
-
-    public CopySpec include(Closure includeSpec) {
-        patternSet.include(includeSpec);
-        return this;
-    }
-
-    public Set<String> getIncludes() {
-        return patternSet.getIncludes();
-    }
-
-    public CopySpec setIncludes(Iterable<String> includes) {
-        patternSet.setIncludes(includes);
-        return this;
-    }
-
-    public List<String> getAllIncludes() {
-        List<String> result = new ArrayList<String>();
-        if (parentSpec != null) {
-            result.addAll(parentSpec.getAllIncludes());
-        }
-        result.addAll(getIncludes());
-        return result;
-    }
-
-    public List<Spec<FileTreeElement>> getAllIncludeSpecs() {
-        List<Spec<FileTreeElement>> result = new ArrayList<Spec<FileTreeElement>>();
-        if (parentSpec != null) {
-            result.addAll(parentSpec.getAllIncludeSpecs());
-        }
-        result.addAll(patternSet.getIncludeSpecs());
-        return result;
-    }
-
-    public CopySpec exclude(String... excludes) {
-        patternSet.exclude(excludes);
-        return this;
-    }
-
-    public CopySpec exclude(Iterable<String> excludes) {
-        patternSet.exclude(excludes);
-        return this;
-    }
-
-    public CopySpec exclude(Spec<FileTreeElement> excludeSpec) {
-        patternSet.exclude(excludeSpec);
-        return this;
-    }
-
-    public CopySpec exclude(Closure excludeSpec) {
-        patternSet.exclude(excludeSpec);
-        return this;
-    }
-
-    public Set<String> getExcludes() {
-        return patternSet.getExcludes();
-    }
-
-    public CopySpecImpl setExcludes(Iterable<String> excludes) {
-        patternSet.setExcludes(excludes);
-        return this;
-    }
-
-    public CopySpec rename(String sourceRegEx, String replaceWith) {
-        actions.add(new RenamingCopyAction(new RegExpNameMapper(sourceRegEx, replaceWith)));
-        return this;
-    }
-
-    public CopySpec rename(Pattern sourceRegEx, String replaceWith) {
-        actions.add(new RenamingCopyAction(new RegExpNameMapper(sourceRegEx, replaceWith)));
-        return this;
-    }
-
-    public CopySpec filter(final Class<? extends FilterReader> filterType) {
-        actions.add(new Action<FileCopyDetails>() {
-            public void execute(FileCopyDetails fileCopyDetails) {
-                fileCopyDetails.filter(filterType);
-            }
-        });
-        return this;
-    }
-
-    public CopySpec filter(final Closure closure) {
-        actions.add(new Action<FileCopyDetails>() {
-            public void execute(FileCopyDetails fileCopyDetails) {
-                fileCopyDetails.filter(closure);
-            }
-        });
-        return this;
-    }
-
-    public CopySpec filter(final Map<String, ?> properties, final Class<? extends FilterReader> filterType) {
-        actions.add(new Action<FileCopyDetails>() {
-            public void execute(FileCopyDetails fileCopyDetails) {
-                fileCopyDetails.filter(properties, filterType);
-            }
-        });
-        return this;
-    }
-
-    public CopySpec expand(final Map<String, ?> properties) {
-        actions.add(new Action<FileCopyDetails>() {
-            public void execute(FileCopyDetails fileCopyDetails) {
-                fileCopyDetails.expand(properties);
-            }
-        });
-        return this;
-    }
-
-    public CopySpec rename(Closure closure) {
-        ChainingTransformer<String> transformer = new ChainingTransformer<String>(String.class);
-        transformer.add(closure);
-        actions.add(new RenamingCopyAction(transformer));
-        return this;
-    }
-
-    public int getDirMode() {
-        if (dirMode != null) {
-            return dirMode;
-        }
-        if (parentSpec != null) {
-            return parentSpec.getDirMode();
-        }
-        return UnixStat.DEFAULT_DIR_PERM;
-    }
-
-    public int getFileMode() {
-        if (fileMode != null) {
-            return fileMode;
-        }
-        if (parentSpec != null) {
-            return parentSpec.getFileMode();
-        }
-        return UnixStat.DEFAULT_FILE_PERM;
-    }
-
-    public CopyProcessingSpec setDirMode(int mode) {
-        dirMode = mode;
-        return this;
-    }
-
-    public CopyProcessingSpec setFileMode(int mode) {
-        fileMode = mode;
-        return this;
-    }
-
-    public CopySpec eachFile(Action<? super FileCopyDetails> action) {
-        actions.add(action);
-        return this;
-    }
-
-    public CopySpec eachFile(Closure closure) {
-        actions.add((Action<? super FileCopyDetails>) DefaultGroovyMethods.asType(closure, Action.class));
-        return this;
-    }
-
-    public List<String> getAllExcludes() {
-        List<String> result = new ArrayList<String>();
-        if (parentSpec != null) {
-            result.addAll(parentSpec.getAllExcludes());
-        }
-        result.addAll(getExcludes());
-        return result;
-    }
-
-    public List<Spec<FileTreeElement>> getAllExcludeSpecs() {
-        List<Spec<FileTreeElement>> result = new ArrayList<Spec<FileTreeElement>>();
-        if (parentSpec != null) {
-            result.addAll(parentSpec.getAllExcludeSpecs());
-        }
-        result.addAll(patternSet.getExcludeSpecs());
-        return result;
-    }
-
-    public List<Action<? super FileCopyDetails>> getAllCopyActions() {
-        if (parentSpec == null) {
-            return actions;
-        }
-        List<Action<? super FileCopyDetails>> allActions = new ArrayList<Action<? super FileCopyDetails>>();
-        allActions.addAll(parentSpec.getAllCopyActions());
-        allActions.addAll(actions);
-        return allActions;
-    }
-
-    public boolean hasSource() {
-        if (!sourcePaths.isEmpty()) {
-            return true;
-        }
-        for (ReadableCopySpec spec : childSpecs) {
-            if (spec.hasSource()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private static class WrapperCopySpec implements ReadableCopySpec {
-        private final ReadableCopySpec root;
-        private final ReadableCopySpec spec;
-
-        public WrapperCopySpec(ReadableCopySpec root, ReadableCopySpec spec) {
-            this.root = root;
-            this.spec = spec;
-        }
-
-        public RelativePath getDestPath() {
-            return root.getDestPath().append(spec.getDestPath());
-        }
-
-        public int getFileMode() {
-            return spec.getFileMode();
-        }
-
-        public int getDirMode() {
-            return spec.getDirMode();
-        }
-
-        public FileTree getSource() {
-            return spec.getSource();
-        }
-
-        public Collection<? extends ReadableCopySpec> getAllSpecs() {
-            List<WrapperCopySpec> specs = new ArrayList<WrapperCopySpec>();
-            for (ReadableCopySpec child : spec.getAllSpecs()) {
-                specs.add(new WrapperCopySpec(root, child));
-            }
-            return specs;
-        }
-
-        public boolean hasSource() {
-            return spec.hasSource();
-        }
-
-        public Collection<? extends Action<? super FileCopyDetails>> getAllCopyActions() {
-            return spec.getAllCopyActions();
-        }
-    }
-}
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 groovy.lang.Closure;
+import org.apache.tools.zip.UnixStat;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+import org.gradle.api.Action;
+import org.gradle.api.file.*;
+import org.gradle.api.internal.ChainingTransformer;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.specs.Spec;
+import org.gradle.api.tasks.util.PatternSet;
+import org.gradle.util.ConfigureUtil;
+
+import java.io.File;
+import java.io.FilterReader;
+import java.util.*;
+import java.util.regex.Pattern;
+
+/**
+ * @author Steve Appling
+ */
+public class CopySpecImpl implements CopySpec, ReadableCopySpec {
+    private final FileResolver resolver;
+    private final Set<Object> sourcePaths;
+    private Object destDir;
+    private final PatternSet patternSet;
+    private final List<ReadableCopySpec> childSpecs;
+    private final CopySpecImpl parentSpec;
+    private final List<Action<? super FileCopyDetails>> actions = new ArrayList<Action<? super FileCopyDetails>>();
+    private Integer dirMode;
+    private Integer fileMode;
+    private Boolean caseSensitive;
+    private Boolean includeEmptyDirs;
+
+    private CopySpecImpl(FileResolver resolver, CopySpecImpl parentSpec) {
+        this.parentSpec = parentSpec;
+        this.resolver = resolver;
+        sourcePaths = new LinkedHashSet<Object>();
+        childSpecs = new ArrayList<ReadableCopySpec>();
+        patternSet = new PatternSet();
+    }
+
+    public CopySpecImpl(FileResolver resolver) {
+        this(resolver, null);
+    }
+
+    protected FileResolver getResolver() {
+        return resolver;
+    }
+
+    public CopySpec with(CopySpec... copySpecs) {
+        for (CopySpec copySpec : copySpecs) {
+            ReadableCopySpec readableCopySpec;
+            if (copySpec instanceof CopySpecSource) {
+                CopySpecSource copySpecSource = (CopySpecSource) copySpec;
+                readableCopySpec = copySpecSource.getRootSpec();
+            } else {
+                readableCopySpec = (ReadableCopySpec) copySpec;
+            }
+            childSpecs.add(new WrapperCopySpec(this, readableCopySpec));
+        }
+        return this;
+    }
+
+    public CopySpec from(Object... sourcePaths) {
+        for (Object sourcePath : sourcePaths) {
+            if (sourcePath instanceof CopySpec) {
+                with((CopySpec) sourcePath);
+            } else {
+                this.sourcePaths.add(sourcePath);
+            }
+        }
+        return this;
+    }
+
+    public CopySpec from(Object sourcePath, Closure c) {
+        if (c == null) {
+            from(sourcePath);
+            return this;
+        } else {
+            CopySpecImpl child = addChild();
+            child.from(sourcePath);
+            ConfigureUtil.configure(c, child);
+            return child;
+        }
+    }
+
+    public CopySpecImpl addFirst() {
+        CopySpecImpl child = new CopySpecImpl(resolver, this);
+        childSpecs.add(0, child);
+        return child;
+    }
+
+    public CopySpecImpl addChild() {
+        CopySpecImpl child = new CopySpecImpl(resolver, this);
+        childSpecs.add(child);
+        return child;
+    }
+
+    public Set<Object> getSourcePaths() {
+        return sourcePaths;
+    }
+
+    public FileTree getSource() {
+        return resolver.resolveFilesAsTree(sourcePaths).matching(getPatternSet());
+    }
+
+    public List<ReadableCopySpec> getAllSpecs() {
+        List<ReadableCopySpec> result = new ArrayList<ReadableCopySpec>();
+        result.add(this);
+        for (ReadableCopySpec childSpec : childSpecs) {
+            result.addAll(childSpec.getAllSpecs());
+        }
+        return result;
+    }
+
+    public CopySpecImpl into(Object destDir) {
+        this.destDir = destDir;
+        return this;
+    }
+
+    public CopySpecImpl into(Object destPath, Closure configureClosure) {
+        if (configureClosure == null) {
+            into(destPath);
+            return this;
+        } else {
+            CopySpecImpl child = addChild();
+            child.into(destPath);
+            ConfigureUtil.configure(configureClosure, child);
+            return child;
+        }
+    }
+
+    public RelativePath getDestPath() {
+        RelativePath parentPath;
+        if (parentSpec == null) {
+            parentPath = new RelativePath(false);
+        } else {
+            parentPath = parentSpec.getDestPath();
+        }
+        if (destDir == null) {
+            return parentPath;
+        }
+
+        String path = resolveToPath(destDir);
+        if (path.startsWith("/") || path.startsWith(File.separator)) {
+            return RelativePath.parse(false, path);
+        }
+
+        return RelativePath.parse(false, parentPath, path);
+    }
+
+    private String resolveToPath(Object destDir) {
+        Object value = destDir;
+        while (true) {
+            if (value instanceof Closure) {
+                Closure closure = (Closure) value;
+                value = closure.call();
+            } else {
+                break;
+            }
+        }
+        return value.toString();
+    }
+
+    public PatternSet getPatternSet() {
+        PatternSet patterns = new PatternSet();
+        patterns.setCaseSensitive(isCaseSensitive());
+        patterns.include(getAllIncludes());
+        patterns.includeSpecs(getAllIncludeSpecs());
+        patterns.exclude(getAllExcludes());
+        patterns.excludeSpecs(getAllExcludeSpecs());
+        return patterns;
+    }
+
+    public boolean isCaseSensitive() {
+        if (caseSensitive != null) {
+            return caseSensitive;
+        }
+        if (parentSpec != null) {
+            return parentSpec.isCaseSensitive();
+        }
+        return true;
+    }
+
+    public void setCaseSensitive(boolean caseSensitive) {
+        this.caseSensitive = caseSensitive;
+    }
+
+    public boolean getIncludeEmptyDirs() {
+        if (includeEmptyDirs != null) {
+            return includeEmptyDirs;
+        }
+        if (parentSpec != null) {
+            return parentSpec.getIncludeEmptyDirs();
+        }
+        return true;
+    }
+
+    public void setIncludeEmptyDirs(boolean includeEmptyDirs) {
+        this.includeEmptyDirs = includeEmptyDirs;
+    }
+
+    public CopySpec include(String... includes) {
+        patternSet.include(includes);
+        return this;
+    }
+
+    public CopySpec include(Iterable<String> includes) {
+        patternSet.include(includes);
+        return this;
+    }
+
+    public CopySpec include(Spec<FileTreeElement> includeSpec) {
+        patternSet.include(includeSpec);
+        return this;
+    }
+
+    public CopySpec include(Closure includeSpec) {
+        patternSet.include(includeSpec);
+        return this;
+    }
+
+    public Set<String> getIncludes() {
+        return patternSet.getIncludes();
+    }
+
+    public CopySpec setIncludes(Iterable<String> includes) {
+        patternSet.setIncludes(includes);
+        return this;
+    }
+
+    public List<String> getAllIncludes() {
+        List<String> result = new ArrayList<String>();
+        if (parentSpec != null) {
+            result.addAll(parentSpec.getAllIncludes());
+        }
+        result.addAll(getIncludes());
+        return result;
+    }
+
+    public List<Spec<FileTreeElement>> getAllIncludeSpecs() {
+        List<Spec<FileTreeElement>> result = new ArrayList<Spec<FileTreeElement>>();
+        if (parentSpec != null) {
+            result.addAll(parentSpec.getAllIncludeSpecs());
+        }
+        result.addAll(patternSet.getIncludeSpecs());
+        return result;
+    }
+
+    public CopySpec exclude(String... excludes) {
+        patternSet.exclude(excludes);
+        return this;
+    }
+
+    public CopySpec exclude(Iterable<String> excludes) {
+        patternSet.exclude(excludes);
+        return this;
+    }
+
+    public CopySpec exclude(Spec<FileTreeElement> excludeSpec) {
+        patternSet.exclude(excludeSpec);
+        return this;
+    }
+
+    public CopySpec exclude(Closure excludeSpec) {
+        patternSet.exclude(excludeSpec);
+        return this;
+    }
+
+    public Set<String> getExcludes() {
+        return patternSet.getExcludes();
+    }
+
+    public CopySpecImpl setExcludes(Iterable<String> excludes) {
+        patternSet.setExcludes(excludes);
+        return this;
+    }
+
+    public CopySpec rename(String sourceRegEx, String replaceWith) {
+        actions.add(new RenamingCopyAction(new RegExpNameMapper(sourceRegEx, replaceWith)));
+        return this;
+    }
+
+    public CopySpec rename(Pattern sourceRegEx, String replaceWith) {
+        actions.add(new RenamingCopyAction(new RegExpNameMapper(sourceRegEx, replaceWith)));
+        return this;
+    }
+
+    public CopySpec filter(final Class<? extends FilterReader> filterType) {
+        actions.add(new Action<FileCopyDetails>() {
+            public void execute(FileCopyDetails fileCopyDetails) {
+                fileCopyDetails.filter(filterType);
+            }
+        });
+        return this;
+    }
+
+    public CopySpec filter(final Closure closure) {
+        actions.add(new Action<FileCopyDetails>() {
+            public void execute(FileCopyDetails fileCopyDetails) {
+                fileCopyDetails.filter(closure);
+            }
+        });
+        return this;
+    }
+
+    public CopySpec filter(final Map<String, ?> properties, final Class<? extends FilterReader> filterType) {
+        actions.add(new Action<FileCopyDetails>() {
+            public void execute(FileCopyDetails fileCopyDetails) {
+                fileCopyDetails.filter(properties, filterType);
+            }
+        });
+        return this;
+    }
+
+    public CopySpec expand(final Map<String, ?> properties) {
+        actions.add(new Action<FileCopyDetails>() {
+            public void execute(FileCopyDetails fileCopyDetails) {
+                fileCopyDetails.expand(properties);
+            }
+        });
+        return this;
+    }
+
+    public CopySpec rename(Closure closure) {
+        ChainingTransformer<String> transformer = new ChainingTransformer<String>(String.class);
+        transformer.add(closure);
+        actions.add(new RenamingCopyAction(transformer));
+        return this;
+    }
+
+    public int getDirMode() {
+        if (dirMode != null) {
+            return dirMode;
+        }
+        if (parentSpec != null) {
+            return parentSpec.getDirMode();
+        }
+        return UnixStat.DEFAULT_DIR_PERM;
+    }
+
+    public int getFileMode() {
+        if (fileMode != null) {
+            return fileMode;
+        }
+        if (parentSpec != null) {
+            return parentSpec.getFileMode();
+        }
+        return UnixStat.DEFAULT_FILE_PERM;
+    }
+
+    public CopyProcessingSpec setDirMode(int mode) {
+        dirMode = mode;
+        return this;
+    }
+
+    public CopyProcessingSpec setFileMode(int mode) {
+        fileMode = mode;
+        return this;
+    }
+
+    public CopySpec eachFile(Action<? super FileCopyDetails> action) {
+        actions.add(action);
+        return this;
+    }
+
+    public CopySpec eachFile(Closure closure) {
+        actions.add((Action<? super FileCopyDetails>) DefaultGroovyMethods.asType(closure, Action.class));
+        return this;
+    }
+
+    public List<String> getAllExcludes() {
+        List<String> result = new ArrayList<String>();
+        if (parentSpec != null) {
+            result.addAll(parentSpec.getAllExcludes());
+        }
+        result.addAll(getExcludes());
+        return result;
+    }
+
+    public List<Spec<FileTreeElement>> getAllExcludeSpecs() {
+        List<Spec<FileTreeElement>> result = new ArrayList<Spec<FileTreeElement>>();
+        if (parentSpec != null) {
+            result.addAll(parentSpec.getAllExcludeSpecs());
+        }
+        result.addAll(patternSet.getExcludeSpecs());
+        return result;
+    }
+
+    public List<Action<? super FileCopyDetails>> getAllCopyActions() {
+        if (parentSpec == null) {
+            return actions;
+        }
+        List<Action<? super FileCopyDetails>> allActions = new ArrayList<Action<? super FileCopyDetails>>();
+        allActions.addAll(parentSpec.getAllCopyActions());
+        allActions.addAll(actions);
+        return allActions;
+    }
+
+    public boolean hasSource() {
+        if (!sourcePaths.isEmpty()) {
+            return true;
+        }
+        for (ReadableCopySpec spec : childSpecs) {
+            if (spec.hasSource()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static class WrapperCopySpec implements ReadableCopySpec {
+        private final ReadableCopySpec root;
+        private final ReadableCopySpec spec;
+
+        public WrapperCopySpec(ReadableCopySpec root, ReadableCopySpec spec) {
+            this.root = root;
+            this.spec = spec;
+        }
+
+        public RelativePath getDestPath() {
+            return root.getDestPath().append(spec.getDestPath());
+        }
+
+        public int getFileMode() {
+            return spec.getFileMode();
+        }
+
+        public int getDirMode() {
+            return spec.getDirMode();
+        }
+
+        public FileTree getSource() {
+            return spec.getSource();
+        }
+
+        public Collection<? extends ReadableCopySpec> getAllSpecs() {
+            List<WrapperCopySpec> specs = new ArrayList<WrapperCopySpec>();
+            for (ReadableCopySpec child : spec.getAllSpecs()) {
+                specs.add(new WrapperCopySpec(root, child));
+            }
+            return specs;
+        }
+
+        public boolean hasSource() {
+            return spec.hasSource();
+        }
+
+        public Collection<? extends Action<? super FileCopyDetails>> getAllCopyActions() {
+            return spec.getAllCopyActions();
+        }
+
+        public boolean getIncludeEmptyDirs() {
+            return spec.getIncludeEmptyDirs();
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopySpecVisitor.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopySpecVisitor.java
index 82036aa..712e986 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopySpecVisitor.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopySpecVisitor.java
@@ -1,53 +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.api.internal.file.copy;
-
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.file.FileTreeElement;
-import org.gradle.api.file.FileVisitDetails;
-
-import java.io.File;
-
-/**
- * @author Steve Appling
- */
-public class FileCopySpecVisitor extends EmptyCopySpecVisitor {
-    private File baseDestDir;
-    private boolean didWork;
-
-    public void startVisit(CopyAction action) {
-        baseDestDir = ((FileCopyAction) action).getDestinationDir();
-        if (baseDestDir == null) {
-            throw new InvalidUserDataException("No copy destination directory has been specified, use 'into' to specify a target directory.");
-        }
-    }
-
-    public void visitFile(FileVisitDetails source) {
-        File target = source.getRelativePath().getFile(baseDestDir);
-        copyFile(source, target);
-    }
-
-    public boolean getDidWork() {
-        return didWork;
-    }
-
-    void copyFile(FileTreeElement srcFile, File destFile) {
-        boolean copied = srcFile.copyTo(destFile);
-        if (copied) {
-            didWork = true;
-        }
-    }
-}
+/*
+ * 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.InvalidUserDataException;
+import org.gradle.api.file.FileTreeElement;
+import org.gradle.api.file.FileVisitDetails;
+
+import java.io.File;
+
+/**
+ * @author Steve Appling
+ */
+public class FileCopySpecVisitor extends EmptyCopySpecVisitor {
+    private File baseDestDir;
+    private boolean didWork;
+
+    public void startVisit(CopyAction action) {
+        baseDestDir = ((FileCopyAction) action).getDestinationDir();
+        if (baseDestDir == null) {
+            throw new InvalidUserDataException("No copy destination directory has been specified, use 'into' to specify a target directory.");
+        }
+    }
+
+    public void visitFile(FileVisitDetails source) {
+        visitFileOrDir(source);
+    }
+
+    public void visitDir(FileVisitDetails source) {
+        visitFileOrDir(source);
+    }
+
+    public boolean getDidWork() {
+        return didWork;
+    }
+
+    private void visitFileOrDir(FileVisitDetails source) {
+        File target = source.getRelativePath().getFile(baseDestDir);
+        copyFile(source, target);
+    }
+
+    private void copyFile(FileTreeElement srcFile, File destFile) {
+        boolean copied = srcFile.copyTo(destFile);
+        if (copied) {
+            didWork = true;
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/LineFilter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/LineFilter.java
index 7766e84..447c7e6 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/LineFilter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/LineFilter.java
@@ -1,109 +1,109 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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 groovy.lang.Closure;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.Reader;
-
-public class LineFilter extends Reader {
-    private final Closure closure;
-    private String transformedLine;
-    private int transformedIndex;
-    private final BufferedReader bufferedIn;
-    private final String lineTerminator;
-    private final Reader in;
-
-    /**
-     * Creates a new filtered reader.
-     *
-     * @param closure a Closure to filter each line
-     * @throws NullPointerException if <code>in</code> is <code>null</code>
-     */
-    public LineFilter(Reader in, Closure closure) {
-        this.in = in;
-        this.bufferedIn = new BufferedReader(in);
-        this.closure = closure;
-        lineTerminator = System.getProperty("line.separator");
-    }
-
-    private String getTransformedLine() throws IOException {
-        StringBuilder line = new StringBuilder();
-        boolean eol = false;
-        int ch;
-        while (!eol && (ch = bufferedIn.read()) >= 0) {
-            if (ch == '\n') {
-                eol = true;
-            } else if (ch == '\r') {
-                eol = true;
-                bufferedIn.mark(1);
-                if (bufferedIn.read() != '\n') {
-                    bufferedIn.reset();
-                }
-            } else {
-                line.append((char) ch);
-            }
-        }
-        if (line.length() == 0 && !eol) {
-            return null;
-        }
-
-        StringBuilder result = new StringBuilder();
-        result.append(closure.call(line.toString()).toString());
-        if (eol) {
-            result.append(lineTerminator);
-        }
-        return result.toString();
-    }
-
-    private void ensureData() throws IOException {
-        if (transformedLine == null || transformedIndex >= transformedLine.length()) {
-            transformedLine = getTransformedLine();
-            transformedIndex = 0;
-        }
-    }
-
-    @Override
-    public int read() throws IOException {
-        ensureData();
-        if (transformedLine == null) {
-            return -1;
-        }
-        return transformedLine.charAt(transformedIndex++);
-    }
-
-    @Override
-    public int read(char[] cbuf, int off, int len) throws IOException {
-        for (int i = 0; i < len; i++) {
-            final int c = read();
-            if (c == -1) {
-                if (i == 0) {
-                    return -1;
-                } else {
-                    return i;
-                }
-            }
-            cbuf[off + i] = (char) c;
-        }
-        return len;
-    }
-
-    public void close() throws IOException {
-        in.close();
-    }
-}
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 groovy.lang.Closure;
+
+import org.gradle.util.SystemProperties;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+
+public class LineFilter extends Reader {
+    private final Closure closure;
+    private String transformedLine;
+    private int transformedIndex;
+    private final BufferedReader bufferedIn;
+    private final Reader in;
+
+    /**
+     * Creates a new filtered reader.
+     *
+     * @param closure a Closure to filter each line
+     * @throws NullPointerException if <code>in</code> is <code>null</code>
+     */
+    public LineFilter(Reader in, Closure closure) {
+        this.in = in;
+        this.bufferedIn = new BufferedReader(in);
+        this.closure = closure;
+    }
+
+    private String getTransformedLine() throws IOException {
+        StringBuilder line = new StringBuilder();
+        boolean eol = false;
+        int ch;
+        while (!eol && (ch = bufferedIn.read()) >= 0) {
+            if (ch == '\n') {
+                eol = true;
+            } else if (ch == '\r') {
+                eol = true;
+                bufferedIn.mark(1);
+                if (bufferedIn.read() != '\n') {
+                    bufferedIn.reset();
+                }
+            } else {
+                line.append((char) ch);
+            }
+        }
+        if (line.length() == 0 && !eol) {
+            return null;
+        }
+
+        StringBuilder result = new StringBuilder();
+        result.append(closure.call(line.toString()).toString());
+        if (eol) {
+            result.append(SystemProperties.getLineSeparator());
+        }
+        return result.toString();
+    }
+
+    private void ensureData() throws IOException {
+        if (transformedLine == null || transformedIndex >= transformedLine.length()) {
+            transformedLine = getTransformedLine();
+            transformedIndex = 0;
+        }
+    }
+
+    @Override
+    public int read() throws IOException {
+        ensureData();
+        if (transformedLine == null || transformedLine.length() == 0) {
+            return -1;
+        }
+        return transformedLine.charAt(transformedIndex++);
+    }
+
+    @Override
+    public int read(char[] cbuf, int off, int len) throws IOException {
+        for (int i = 0; i < len; i++) {
+            final int c = read();
+            if (c == -1) {
+                if (i == 0) {
+                    return -1;
+                } else {
+                    return i;
+                }
+            }
+            cbuf[off + i] = (char) c;
+        }
+        return len;
+    }
+
+    public void close() throws IOException {
+        in.close();
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/NormalizingCopySpecVisitor.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/NormalizingCopySpecVisitor.java
index 3ea4912..f35c3e6 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/NormalizingCopySpecVisitor.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/NormalizingCopySpecVisitor.java
@@ -21,16 +21,14 @@ import org.gradle.api.internal.file.AbstractFileTreeElement;
 
 import java.io.File;
 import java.io.InputStream;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 
 /**
- * A {@link CopySpecVisitor} which cleans up the tree as it is visited. Removes duplicate and empty directories and
- * adds in missing directories.
+ * A {@link CopySpecVisitor} which cleans up the tree as it is visited. Removes duplicate directories and
+ * adds in missing directories. Removes empty directories if instructed to do so by copy spec.
  */
 public class NormalizingCopySpecVisitor extends DelegatingCopySpecVisitor {
+    private ReadableCopySpec spec;
     private final Set<RelativePath> visitedDirs = new HashSet<RelativePath>();
     private final Map<RelativePath, FileVisitDetails> pendingDirs = new HashMap<RelativePath, FileVisitDetails>();
 
@@ -38,7 +36,18 @@ public class NormalizingCopySpecVisitor extends DelegatingCopySpecVisitor {
         super(visitor);
     }
 
+    @Override
+    public void visitSpec(ReadableCopySpec spec) {
+        this.spec = spec;
+        getVisitor().visitSpec(spec);
+    }
+
     public void endVisit() {
+        if (spec.getIncludeEmptyDirs()) {
+            for (RelativePath path : new ArrayList<RelativePath>(pendingDirs.keySet())) {
+                maybeVisit(path);
+            }
+        }
         visitedDirs.clear();
         pendingDirs.clear();
         getVisitor().endVisit();
@@ -90,7 +99,7 @@ public class NormalizingCopySpecVisitor extends DelegatingCopySpecVisitor {
         }
 
         public boolean isDirectory() {
-            throw new UnsupportedOperationException();
+            return !path.isFile();
         }
 
         public long getLastModified() {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/ReadableCopySpec.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/ReadableCopySpec.java
index 0d58a8e..67aad67 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/ReadableCopySpec.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/ReadableCopySpec.java
@@ -36,4 +36,6 @@ public interface ReadableCopySpec {
     boolean hasSource();
 
     Collection<? extends Action<? super FileCopyDetails>> getAllCopyActions();
+
+    boolean getIncludeEmptyDirs();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/SyncCopySpecVisitor.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/SyncCopySpecVisitor.java
index 7eda366..676ce9a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/SyncCopySpecVisitor.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/SyncCopySpecVisitor.java
@@ -18,8 +18,8 @@ package org.gradle.api.internal.file.copy;
 import org.gradle.api.file.FileVisitDetails;
 import org.gradle.api.file.FileVisitor;
 import org.gradle.api.file.RelativePath;
-import org.gradle.api.internal.file.DefaultDirectoryWalker;
-import org.gradle.api.internal.file.DirectoryWalker;
+import org.gradle.api.internal.file.collections.DirectoryFileTree;
+import org.gradle.api.internal.file.collections.MinimalFileTree;
 import org.gradle.util.GFileUtils;
 
 import java.io.File;
@@ -76,8 +76,8 @@ public class SyncCopySpecVisitor extends DelegatingCopySpecVisitor {
             }
         };
 
-        DirectoryWalker walker = new DefaultDirectoryWalker(visitor).depthFirst();
-        walker.start(baseDestDir);
+        MinimalFileTree walker = new DirectoryFileTree(baseDestDir).depthFirst();
+        walker.visit(visitor);
         visited.clear();
 
         getVisitor().endVisit();
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/DefaultScriptHandlerFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/DefaultScriptHandlerFactory.java
index bdf5372..a72d432 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/DefaultScriptHandlerFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/DefaultScriptHandlerFactory.java
@@ -1,89 +1,89 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.initialization;
-
-import org.gradle.api.Project;
-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.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.groovy.scripts.ScriptSource;
-import org.gradle.util.ObservableUrlClassLoader;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
-public class DefaultScriptHandlerFactory implements ScriptHandlerFactory {
-    private final Factory<? extends RepositoryHandler> repositoryHandlerFactory;
-    private final ConfigurationContainerFactory configurationContainerFactory;
-    private final DependencyMetaDataProvider dependencyMetaDataProvider;
-    private final DependencyFactory dependencyFactory;
-    private final Map<Collection<Object>, ObservableUrlClassLoader> classLoaderCache = new HashMap<Collection<Object>, ObservableUrlClassLoader>();  
-    private final ProjectFinder projectFinder = new ProjectFinder() {
-        public Project getProject(String path) {
-            throw new UnknownProjectException("Cannot use project dependencies in a script classpath definition.");
-        }
-    };
-
-    public DefaultScriptHandlerFactory(Factory<? extends RepositoryHandler> repositoryHandlerFactory,
-                                       ConfigurationContainerFactory configurationContainerFactory,
-                                       DependencyMetaDataProvider dependencyMetaDataProvider,
-                                       DependencyFactory dependencyFactory) {
-        this.repositoryHandlerFactory = repositoryHandlerFactory;
-        this.configurationContainerFactory = configurationContainerFactory;
-        this.dependencyMetaDataProvider = dependencyMetaDataProvider;
-        this.dependencyFactory = dependencyFactory;
-    }
-
-    public ScriptHandlerInternal create(ScriptSource scriptSource, ClassLoader parentClassLoader) {
-        return create(scriptSource, parentClassLoader, new BasicDomainObjectContext());
-    }
-
-    public ScriptHandlerInternal create(ScriptSource scriptSource, ClassLoader parentClassLoader,
-                                        DomainObjectContext context) {
-        RepositoryHandler repositoryHandler = repositoryHandlerFactory.create();
-        ConfigurationContainer configurationContainer = configurationContainerFactory.createConfigurationContainer(
-                repositoryHandler, dependencyMetaDataProvider, context);
-        DependencyHandler dependencyHandler = new DefaultDependencyHandler(configurationContainer, dependencyFactory,
-                projectFinder);
-        Collection<Object> key = Arrays.asList(scriptSource.getClassName(), parentClassLoader);
-        ObservableUrlClassLoader classLoader = classLoaderCache.get(key);
-        if (classLoader == null) {
-            classLoader = new ObservableUrlClassLoader(parentClassLoader);
-            classLoaderCache.put(key, classLoader);
-            return new DefaultScriptHandler(scriptSource, repositoryHandler, dependencyHandler, configurationContainer,
-                    classLoader);
-        }
-        
-        return new NoClassLoaderUpdateScriptHandler(classLoader, repositoryHandler, dependencyHandler, scriptSource, configurationContainer);
-    }
-
-    private static class BasicDomainObjectContext implements DomainObjectContext {
-        public String absoluteProjectPath(String name) {
-            return name;
-        }
-    }
-}
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.initialization;
+
+import org.gradle.api.Project;
+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.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.groovy.scripts.ScriptSource;
+import org.gradle.util.ObservableUrlClassLoader;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+public class DefaultScriptHandlerFactory implements ScriptHandlerFactory {
+    private final Factory<RepositoryHandler> repositoryHandlerFactory;
+    private final ConfigurationContainerFactory configurationContainerFactory;
+    private final DependencyMetaDataProvider dependencyMetaDataProvider;
+    private final DependencyFactory dependencyFactory;
+    private final Map<Collection<Object>, ObservableUrlClassLoader> classLoaderCache = new HashMap<Collection<Object>, ObservableUrlClassLoader>();  
+    private final ProjectFinder projectFinder = new ProjectFinder() {
+        public Project getProject(String path) {
+            throw new UnknownProjectException("Cannot use project dependencies in a script classpath definition.");
+        }
+    };
+
+    public DefaultScriptHandlerFactory(Factory<RepositoryHandler> repositoryHandlerFactory,
+                                       ConfigurationContainerFactory configurationContainerFactory,
+                                       DependencyMetaDataProvider dependencyMetaDataProvider,
+                                       DependencyFactory dependencyFactory) {
+        this.repositoryHandlerFactory = repositoryHandlerFactory;
+        this.configurationContainerFactory = configurationContainerFactory;
+        this.dependencyMetaDataProvider = dependencyMetaDataProvider;
+        this.dependencyFactory = dependencyFactory;
+    }
+
+    public ScriptHandlerInternal create(ScriptSource scriptSource, ClassLoader parentClassLoader) {
+        return create(scriptSource, parentClassLoader, new BasicDomainObjectContext());
+    }
+
+    public ScriptHandlerInternal create(ScriptSource scriptSource, ClassLoader parentClassLoader,
+                                        DomainObjectContext context) {
+        RepositoryHandler repositoryHandler = repositoryHandlerFactory.create();
+        ConfigurationContainer configurationContainer = configurationContainerFactory.createConfigurationContainer(
+                repositoryHandler, dependencyMetaDataProvider, context);
+        DependencyHandler dependencyHandler = new DefaultDependencyHandler(configurationContainer, dependencyFactory,
+                projectFinder);
+        Collection<Object> key = Arrays.asList(scriptSource.getClassName(), parentClassLoader);
+        ObservableUrlClassLoader classLoader = classLoaderCache.get(key);
+        if (classLoader == null) {
+            classLoader = new ObservableUrlClassLoader(parentClassLoader);
+            classLoaderCache.put(key, classLoader);
+            return new DefaultScriptHandler(scriptSource, repositoryHandler, dependencyHandler, configurationContainer,
+                    classLoader);
+        }
+        
+        return new NoClassLoaderUpdateScriptHandler(classLoader, repositoryHandler, dependencyHandler, scriptSource, configurationContainer);
+    }
+
+    private static class BasicDomainObjectContext implements DomainObjectContext {
+        public String absoluteProjectPath(String name) {
+            return name;
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/IdePlugin.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/IdePlugin.java
deleted file mode 100644
index 593d069..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/IdePlugin.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.api.internal.plugins;
-
-import org.apache.commons.lang.StringUtils;
-import org.gradle.api.Plugin;
-import org.gradle.api.Project;
-import org.gradle.api.Task;
-import org.gradle.api.tasks.Delete;
-
-public abstract class IdePlugin implements Plugin<Project> {
-    private Task lifecycleTask;
-    private Task cleanTask;
-    private Project project;
-
-    public void apply(Project target) {
-        project = target;
-        String lifecyleTaskName = getLifecycleTaskName();
-        lifecycleTask = target.task(lifecyleTaskName);
-        lifecycleTask.setGroup("IDE");
-        cleanTask = target.task(cleanName(lifecyleTaskName));
-        cleanTask.setGroup("IDE");
-        onApply(target);
-    }
-
-    public Task getLifecycleTask() {
-        return lifecycleTask;
-    }
-
-    public Task getCleanTask() {
-        return cleanTask;
-    }
-
-    public Task getCleanTask(Task worker) {
-        return project.getTasks().getByName(cleanName(worker.getName()));
-    }
-
-    private String cleanName(String taskName) {
-        return String.format("clean%s", StringUtils.capitalize(taskName));
-    }
-
-    protected void addWorker(Task worker) {
-        lifecycleTask.dependsOn(worker);
-        Delete cleanWorker = project.getTasks().add(cleanName(worker.getName()), Delete.class);
-        cleanWorker.delete(worker.getOutputs().getFiles());
-        cleanTask.dependsOn(cleanWorker);
-    }
-    
-    protected void onApply(Project target) {
-    }
-
-    protected abstract String getLifecycleTaskName();
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/AbstractProject.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/AbstractProject.java
index f5c902e..6d953e9 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/AbstractProject.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/AbstractProject.java
@@ -103,7 +103,7 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
     private FileResolver fileResolver;
     private FileOperations fileOperations;
 
-    private Factory<? extends AntBuilder> antBuilderFactory;
+    private Factory<AntBuilder> antBuilderFactory;
 
     private AntBuilder ant;
 
@@ -125,7 +125,7 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
 
     private ArtifactHandler artifactHandler;
 
-    private Factory<? extends RepositoryHandler> repositoryHandlerFactory;
+    private Factory<RepositoryHandler> repositoryHandlerFactory;
 
     private RepositoryHandler repositoryHandler;
 
@@ -360,7 +360,7 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
         return repositoryHandler;
     }
 
-    public Factory<? extends RepositoryHandler> getRepositoryHandlerFactory() {
+    public Factory<RepositoryHandler> getRepositoryHandlerFactory() {
         return repositoryHandlerFactory;
     }
 
@@ -401,25 +401,6 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
         return projectRegistry;
     }
 
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        AbstractProject that = (AbstractProject) o;
-
-        return path.equals(that.path);
-    }
-
-    @Override
-    public int hashCode() {
-        return path.hashCode();
-    }
-
     public int depthCompare(Project otherProject) {
         return new Integer(getDepth()).compareTo(otherProject.getDepth());
     }
@@ -761,11 +742,11 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
         this.taskContainer = taskContainer;
     }
 
-    public Factory<? extends AntBuilder> getAntBuilderFactory() {
+    public Factory<AntBuilder> getAntBuilderFactory() {
         return antBuilderFactory;
     }
 
-    public void setAntBuilderFactory(Factory<? extends AntBuilder> antBuilderFactory) {
+    public void setAntBuilderFactory(Factory<AntBuilder> antBuilderFactory) {
         this.antBuilderFactory = antBuilderFactory;
     }
 
@@ -964,7 +945,7 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
         return classGenerator.newInstance(DefaultAutoCreateDomainObjectContainer.class, type, classGenerator);
     }
 
-    public <T> NamedDomainObjectContainer<T> container(Class<T> type, NamedDomainObjectFactory<? extends T> factory) {
+    public <T> NamedDomainObjectContainer<T> container(Class<T> type, NamedDomainObjectFactory<T> factory) {
         ClassGenerator classGenerator = getServices().get(ClassGenerator.class);
         return classGenerator.newInstance(DefaultAutoCreateDomainObjectContainer.class, type, classGenerator, factory);
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultServiceRegistry.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultServiceRegistry.java
index 2da01d6..81e1d9d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultServiceRegistry.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultServiceRegistry.java
@@ -141,14 +141,14 @@ public class DefaultServiceRegistry implements ServiceRegistry {
                 serviceType.getSimpleName(), this));
     }
 
-    public <T> Factory<? extends T> getFactory(Class<T> type) {
+    public <T> Factory<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);
+            Factory<T> factory = service.getFactory(type);
             if (factory != null) {
                 return factory;
             }
@@ -231,7 +231,7 @@ public class DefaultServiceRegistry implements ServiceRegistry {
             }
         }
 
-        public <T> Factory<? extends T> getFactory(Class<T> elementType) {
+        public <T> Factory<T> getFactory(Class<T> elementType) {
             if (!Factory.class.isAssignableFrom(serviceClass)) {
                 return null;
             }
@@ -314,7 +314,7 @@ public class DefaultServiceRegistry implements ServiceRegistry {
             if (Factory.class.isAssignableFrom(method.getParameterTypes()[0])) {
                 ParameterizedType fatoryType = (ParameterizedType) method.getGenericParameterTypes()[0];
                 Type typeArg = fatoryType.getActualTypeArguments()[0];
-                Class type;
+                Class<?> type;
                 if (typeArg instanceof WildcardType) {
                     WildcardType wildcardType = (WildcardType) typeArg;
                     type = (Class) wildcardType.getUpperBounds()[0];
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/GlobalServicesRegistry.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/GlobalServicesRegistry.java
index 7776877..9731d2f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/GlobalServicesRegistry.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/GlobalServicesRegistry.java
@@ -37,7 +37,7 @@ import org.gradle.logging.LoggingServiceRegistry;
  */
 public class GlobalServicesRegistry extends DefaultServiceRegistry {
     public GlobalServicesRegistry() {
-        this(new LoggingServiceRegistry());
+        this(LoggingServiceRegistry.newCommandLineProcessLogging());
     }
 
     public GlobalServicesRegistry(ServiceRegistry loggingServices) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ProjectInternalServiceRegistry.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ProjectInternalServiceRegistry.java
index 1b9ef0d..a7d5d3c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ProjectInternalServiceRegistry.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ProjectInternalServiceRegistry.java
@@ -104,7 +104,7 @@ public class ProjectInternalServiceRegistry extends DefaultServiceRegistry imple
         return new DefaultConvention();
     }
 
-    protected Factory<RepositoryHandler> createRepositoryHandlerFactory(Factory<? extends RepositoryHandler> factory) {
+    protected Factory<RepositoryHandler> createRepositoryHandlerFactory(Factory<RepositoryHandler> factory) {
         return new SharedConventionRepositoryHandlerFactory(factory, get(Convention.class));
     }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ServiceRegistry.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ServiceRegistry.java
index 0e07154..e0449af 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ServiceRegistry.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ServiceRegistry.java
@@ -39,7 +39,7 @@ public interface ServiceRegistry {
      * @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;
+    <T> Factory<T> getFactory(Class<T> type) throws IllegalArgumentException;
 
     /**
      * Creates a new service instance of the given type.
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/TaskInternalServiceRegistry.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/TaskInternalServiceRegistry.java
index 4adbd39..cd8745b 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/TaskInternalServiceRegistry.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/TaskInternalServiceRegistry.java
@@ -37,7 +37,7 @@ public class TaskInternalServiceRegistry extends DefaultServiceRegistry implemen
     }
 
     protected TaskInputs createTaskInputs() {
-        return new DefaultTaskInputs(project.getFileResolver());
+        return new DefaultTaskInputs(project.getFileResolver(), taskInternal);
     }
 
     protected TaskOutputsInternal createTaskOutputs() {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/TopLevelBuildServiceRegistry.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/TopLevelBuildServiceRegistry.java
index e9218f5..9f38832 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/TopLevelBuildServiceRegistry.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/TopLevelBuildServiceRegistry.java
@@ -22,11 +22,13 @@ import org.gradle.StartParameter;
 import org.gradle.api.Project;
 import org.gradle.api.artifacts.Module;
 import org.gradle.api.artifacts.dsl.RepositoryHandler;
+import org.gradle.api.artifacts.maven.MavenFactory;
 import org.gradle.api.execution.TaskActionListener;
 import org.gradle.api.internal.*;
 import org.gradle.api.internal.artifacts.ConfigurationContainerFactory;
 import org.gradle.api.internal.artifacts.DefaultConfigurationContainerFactory;
 import org.gradle.api.internal.artifacts.DefaultModule;
+import org.gradle.api.internal.artifacts.DependencyManagementServices;
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
 import org.gradle.api.internal.artifacts.dsl.DefaultPublishArtifactFactory;
 import org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandlerFactory;
@@ -40,11 +42,12 @@ import org.gradle.api.internal.changedetection.*;
 import org.gradle.api.internal.file.IdentityFileResolver;
 import org.gradle.api.internal.initialization.DefaultScriptHandlerFactory;
 import org.gradle.api.internal.initialization.ScriptHandlerFactory;
-import org.gradle.api.internal.project.taskfactory.*;
-import org.gradle.api.internal.tasks.DefaultTaskExecuter;
-import org.gradle.api.internal.tasks.ExecuteAtMostOnceTaskExecuter;
-import org.gradle.api.internal.tasks.SkipTaskExecuter;
+import org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory;
+import org.gradle.api.internal.project.taskfactory.DependencyAutoWireTaskFactory;
+import org.gradle.api.internal.project.taskfactory.ITaskFactory;
+import org.gradle.api.internal.project.taskfactory.TaskFactory;
 import org.gradle.api.internal.tasks.TaskExecuter;
+import org.gradle.api.internal.tasks.execution.*;
 import org.gradle.cache.AutoCloseCacheFactory;
 import org.gradle.cache.CacheFactory;
 import org.gradle.cache.CacheRepository;
@@ -126,18 +129,20 @@ public class TopLevelBuildServiceRegistry extends DefaultServiceRegistry impleme
 
     protected TaskExecuter createTaskExecuter() {
         return new ExecuteAtMostOnceTaskExecuter(
-                new SkipTaskExecuter(
-                        new ExecutionShortCircuitTaskExecuter(
-                                new PostExecutionAnalysisTaskExecuter(
-                                        new DefaultTaskExecuter(
-                                                get(ListenerManager.class).getBroadcaster(TaskActionListener.class))),
-                                        get(TaskArtifactStateRepository.class))));
+                new SkipOnlyIfTaskExecuter(
+                        new SkipTaskWithNoActionsExecuter(
+                                new SkipEmptySourceFilesTaskExecuter(
+                                        new ValidatingTaskExecuter(
+                                                new SkipUpToDateTaskExecuter(
+                                                        new PostExecutionAnalysisTaskExecuter(
+                                                                new ExecuteActionsTaskExecuter(
+                                                                        get(ListenerManager.class).getBroadcaster(TaskActionListener.class))),
+                                                        get(TaskArtifactStateRepository.class)))))));
     }
 
     protected Factory<RepositoryHandler> createRepositoryHandlerFactory() {
         return new DefaultRepositoryHandlerFactory(
-                new DefaultResolverFactory(
-                        getFactory(LoggingManagerInternal.class)),
+                get(DependencyManagementServices.class).get(ResolverFactory.class),
                 get(ClassGenerator.class));
     }
 
@@ -254,11 +259,13 @@ public class TopLevelBuildServiceRegistry extends DefaultServiceRegistry impleme
                         cacheRepository));
 
         FileSnapshotter outputFilesSnapshotter = new OutputFilesSnapshotter(fileSnapshotter, new RandomLongIdGenerator(), cacheRepository);
-        return new ShortCircuitTaskArtifactStateRepository(
-                startParameter,
-                new DefaultTaskArtifactStateRepository(cacheRepository,
-                        fileSnapshotter,
-                        outputFilesSnapshotter));
+        return new FileCacheBroadcastTaskArtifactStateRepository(
+                new ShortCircuitTaskArtifactStateRepository(
+                        startParameter,
+                        new DefaultTaskArtifactStateRepository(cacheRepository,
+                                fileSnapshotter,
+                                outputFilesSnapshotter)),
+                new DefaultFileCacheListener());
     }
 
     protected ScriptCompilerFactory createScriptCompileFactory() {
@@ -330,6 +337,20 @@ public class TopLevelBuildServiceRegistry extends DefaultServiceRegistry impleme
                 new ImplicitTasksConfigurer());
     }
 
+    protected MavenFactory createMavenFactory() {
+        return get(DependencyManagementServices.class).get(MavenFactory.class);
+    }
+
+    protected DependencyManagementServices createDependencyManagementServices() {
+        ClassLoader coreImplClassLoader = get(ClassLoaderFactory.class).getCoreImplClassLoader();
+        try {
+            Class<?> implClass = coreImplClassLoader.loadClass("org.gradle.api.internal.artifacts.DefaultDependencyManagementServices");
+            return (DependencyManagementServices) implClass.getConstructor(ServiceRegistry.class).newInstance(this);
+        } catch (Exception e) {
+            throw UncheckedException.asUncheckedException(e);
+        }
+    }
+
     public ServiceRegistryFactory createFor(Object domainObject) {
         if (domainObject instanceof GradleInternal) {
             return new GradleInternalServiceRegistry(this, (GradleInternal) domainObject);
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/AnnotationProcessingTaskFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/AnnotationProcessingTaskFactory.java
index b8a94e8..398baa3 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/AnnotationProcessingTaskFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/AnnotationProcessingTaskFactory.java
@@ -18,10 +18,10 @@ package org.gradle.api.internal.project.taskfactory;
 import org.apache.commons.lang.StringUtils;
 import org.gradle.api.Action;
 import org.gradle.api.GradleException;
-import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.Task;
 import org.gradle.api.internal.TaskInternal;
 import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.tasks.execution.TaskValidator;
 import org.gradle.api.tasks.Optional;
 import org.gradle.api.tasks.TaskAction;
 import org.gradle.util.ReflectionUtil;
@@ -35,8 +35,7 @@ import java.util.*;
 import java.util.concurrent.Callable;
 
 /**
- * A {@link ITaskFactory} which determines task actions, inputs and outputs based on annotation attached to the task
- * properties. Also provides some validation based on these annotations.
+ * A {@link ITaskFactory} which determines task actions, inputs and outputs based on annotation attached to the task properties. Also provides some validation based on these annotations.
  */
 public class AnnotationProcessingTaskFactory implements ITaskFactory {
     private final ITaskFactory taskFactory;
@@ -50,10 +49,9 @@ public class AnnotationProcessingTaskFactory implements ITaskFactory {
             new InputPropertyAnnotationHandler(),
             new NestedBeanPropertyAnnotationHandler());
     private final ValidationAction notNullValidator = new ValidationAction() {
-        public void validate(String propertyName, Object value) throws InvalidUserDataException {
+        public void validate(String propertyName, Object value, Collection<String> messages) {
             if (value == null) {
-                throw new InvalidUserDataException(String.format("No value has been specified for property '%s'.",
-                        propertyName));
+                messages.add(String.format("No value has been specified for property '%s'.", propertyName));
             }
         }
     };
@@ -138,10 +136,11 @@ public class AnnotationProcessingTaskFactory implements ITaskFactory {
                 && method.getParameterTypes().length == 0 && !Modifier.isStatic(method.getModifiers());
     }
 
-    private class Validator implements Action<Task> {
+    private class Validator implements Action<Task>, TaskValidator {
         private Set<PropertyInfo> properties = new LinkedHashSet<PropertyInfo>();
 
-        public void addInputsAndOutputs(final Task task) {
+        public void addInputsAndOutputs(final TaskInternal task) {
+            task.addValidator(this);
             for (final PropertyInfo property : properties) {
                 Callable<Object> futureValue = new Callable<Object>() {
                     public Object call() throws Exception {
@@ -154,22 +153,18 @@ public class AnnotationProcessingTaskFactory implements ITaskFactory {
         }
 
         public void execute(Task task) {
-            try {
-                List<PropertyValue> propertyValues = new ArrayList<PropertyValue>();
-                for (PropertyInfo property : properties) {
-                    propertyValues.add(property.getValue(task));
-                }
-                for (PropertyValue propertyValue : propertyValues) {
-                    propertyValue.checkNotNull();
-                }
-                for (PropertyValue propertyValue : propertyValues) {
-                    propertyValue.checkSkip();
-                }
-                for (PropertyValue propertyValue : propertyValues) {
-                    propertyValue.checkValid();
-                }
-            } catch (InvalidUserDataException e) {
-                throw new InvalidUserDataException(String.format("Error validating %s: %s", task, e.getMessage()), e);
+        }
+
+        public void validate(TaskInternal task, Collection<String> messages) {
+            List<PropertyValue> propertyValues = new ArrayList<PropertyValue>();
+            for (PropertyInfo property : properties) {
+                propertyValues.add(property.getValue(task));
+            }
+            for (PropertyValue propertyValue : propertyValues) {
+                propertyValue.checkNotNull(messages);
+            }
+            for (PropertyValue propertyValue : propertyValues) {
+                propertyValue.checkValid(messages);
             }
         }
 
@@ -236,16 +231,14 @@ public class AnnotationProcessingTaskFactory implements ITaskFactory {
     private interface PropertyValue {
         Object getValue();
 
-        void checkNotNull();
+        void checkNotNull(Collection<String> messages);
 
-        void checkSkip();
-
-        void checkValid();
+        void checkValid(Collection<String> messages);
     }
 
     private static class PropertyInfo implements PropertyActionContext {
         private static final ValidationAction NO_OP_VALIDATION_ACTION = new ValidationAction() {
-            public void validate(String propertyName, Object value) throws InvalidUserDataException {
+            public void validate(String propertyName, Object value, Collection<String> messages) {
             }
         };
         private static final PropertyValue NO_OP_VALUE = new PropertyValue() {
@@ -253,13 +246,10 @@ public class AnnotationProcessingTaskFactory implements ITaskFactory {
                 return null;
             }
 
-            public void checkNotNull() {
-            }
-
-            public void checkSkip() {
+            public void checkNotNull(Collection<String> messages) {
             }
 
-            public void checkValid() {
+            public void checkValid(Collection<String> messages) {
             }
         };
         private static final UpdateAction NO_OP_CONFIGURATION_ACTION = new UpdateAction() {
@@ -271,7 +261,6 @@ public class AnnotationProcessingTaskFactory implements ITaskFactory {
         private final PropertyInfo parent;
         private final String propertyName;
         private final Method method;
-        private ValidationAction skipAction = NO_OP_VALIDATION_ACTION;
         private ValidationAction validationAction = NO_OP_VALIDATION_ACTION;
         private ValidationAction notNullValidator = NO_OP_VALIDATION_ACTION;
         private UpdateAction configureAction = NO_OP_CONFIGURATION_ACTION;
@@ -301,10 +290,6 @@ public class AnnotationProcessingTaskFactory implements ITaskFactory {
             return method;
         }
 
-        public void setSkipAction(ValidationAction action) {
-            skipAction = action;
-        }
-
         public void setValidationAction(ValidationAction action) {
             validationAction = action;
         }
@@ -338,17 +323,13 @@ public class AnnotationProcessingTaskFactory implements ITaskFactory {
                     return value;
                 }
 
-                public void checkNotNull() {
-                    notNullValidator.validate(propertyName, value);
-                }
-
-                public void checkSkip() {
-                    skipAction.validate(propertyName, value);
+                public void checkNotNull(Collection<String> messages) {
+                    notNullValidator.validate(propertyName, value, messages);
                 }
 
-                public void checkValid() {
+                public void checkValid(Collection<String> messages) {
                     if (value != null) {
-                        validationAction.validate(propertyName, value);
+                        validationAction.validate(propertyName, value, messages);
                     }
                 }
             };
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/ExecutionShortCircuitTaskExecuter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/ExecutionShortCircuitTaskExecuter.java
deleted file mode 100644
index 660b7be..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/ExecutionShortCircuitTaskExecuter.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.api.internal.project.taskfactory;
-
-import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.internal.changedetection.TaskArtifactState;
-import org.gradle.api.internal.changedetection.TaskArtifactStateRepository;
-import org.gradle.api.internal.tasks.TaskExecuter;
-import org.gradle.api.internal.tasks.TaskStateInternal;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class ExecutionShortCircuitTaskExecuter implements TaskExecuter {
-    private static final Logger LOGGER = LoggerFactory.getLogger(ExecutionShortCircuitTaskExecuter.class);
-    private final TaskExecuter executer;
-    private final TaskArtifactStateRepository repository;
-
-    public ExecutionShortCircuitTaskExecuter(TaskExecuter executer, TaskArtifactStateRepository repository) {
-        this.executer = executer;
-        this.repository = repository;
-    }
-
-    public void execute(TaskInternal task, TaskStateInternal state) {
-        LOGGER.debug("Determining if {} is up-to-date", task);
-        TaskArtifactState taskArtifactState = repository.getStateFor(task);
-        if (taskArtifactState.isUpToDate()) {
-            LOGGER.debug("{} is up-to-date", task);
-            state.upToDate();
-            return;
-
-        }
-        LOGGER.debug("{} is not up-to-date", task);
-
-        task.getOutputs().setHistory(taskArtifactState);
-        try {
-            executer.execute(task, state);
-            if (state.getFailure() == null) {
-                taskArtifactState.update();
-            }
-        } finally {
-            task.getOutputs().setHistory(null);
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputDirectoryPropertyAnnotationHandler.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputDirectoryPropertyAnnotationHandler.java
index 049552e..43a0ba4 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputDirectoryPropertyAnnotationHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputDirectoryPropertyAnnotationHandler.java
@@ -15,36 +15,24 @@
  */
 package org.gradle.api.internal.project.taskfactory;
 
-import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.Task;
 import org.gradle.api.file.ConfigurableFileTree;
 import org.gradle.api.tasks.InputDirectory;
 import org.gradle.api.tasks.SkipWhenEmpty;
-import org.gradle.api.tasks.StopExecutionException;
 
 import java.io.File;
 import java.lang.annotation.Annotation;
+import java.util.Collection;
 import java.util.concurrent.Callable;
 
 public class InputDirectoryPropertyAnnotationHandler implements PropertyAnnotationHandler {
     private final ValidationAction inputDirValidation = new ValidationAction() {
-        public void validate(String propertyName, Object value) throws InvalidUserDataException {
+        public void validate(String propertyName, Object value, Collection<String> messages) {
             File fileValue = (value instanceof ConfigurableFileTree) ? ((ConfigurableFileTree) value).getDir() : (File) value;
             if (!fileValue.exists()) {
-                throw new InvalidUserDataException(String.format(
-                        "Directory '%s' specified for property '%s' does not exist.", fileValue, propertyName));
-            }
-            if (!fileValue.isDirectory()) {
-                throw new InvalidUserDataException(String.format(
-                        "Directory '%s' specified for property '%s' is not a directory.", fileValue, propertyName));
-            }
-        }
-    };
-    private final ValidationAction skipEmptyDirectoryAction = new ValidationAction() {
-        public void validate(String propertyName, Object value) throws InvalidUserDataException {
-            File fileValue = (File) value;
-            if (!fileValue.exists() || fileValue.isDirectory() && fileValue.list().length == 0) {
-                throw new StopExecutionException(String.format("Directory %s is empty or does not exist.", fileValue));
+                messages.add(String.format("Directory '%s' specified for property '%s' does not exist.", fileValue, propertyName));
+            } else if (!fileValue.isDirectory()) {
+                messages.add(String.format("Directory '%s' specified for property '%s' is not a directory.", fileValue, propertyName));
             }
         }
     };
@@ -55,12 +43,14 @@ public class InputDirectoryPropertyAnnotationHandler implements PropertyAnnotati
 
     public void attachActions(PropertyActionContext context) {
         context.setValidationAction(inputDirValidation);
-        if (context.getTarget().getAnnotation(SkipWhenEmpty.class) != null) {
-            context.setSkipAction(skipEmptyDirectoryAction);
-        }
+        final boolean isSourceDir = context.getTarget().getAnnotation(SkipWhenEmpty.class) != null;
         context.setConfigureAction(new UpdateAction() {
             public void update(Task task, Callable<Object> futureValue) {
-                task.getInputs().dir(futureValue);
+                if (isSourceDir) {
+                    task.getInputs().sourceDir(futureValue);
+                } else {
+                    task.getInputs().dir(futureValue);
+                }
             }
         });
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputFilePropertyAnnotationHandler.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputFilePropertyAnnotationHandler.java
index ea430c1..f31b7b5 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputFilePropertyAnnotationHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputFilePropertyAnnotationHandler.java
@@ -15,25 +15,22 @@
  */
 package org.gradle.api.internal.project.taskfactory;
 
-import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.Task;
 import org.gradle.api.tasks.InputFile;
 
 import java.io.File;
 import java.lang.annotation.Annotation;
+import java.util.Collection;
 import java.util.concurrent.Callable;
 
 public class InputFilePropertyAnnotationHandler implements PropertyAnnotationHandler {
     private final ValidationAction inputFileValidation = new ValidationAction() {
-        public void validate(String propertyName, Object value) throws InvalidUserDataException {
+        public void validate(String propertyName, Object value, Collection<String> messages) {
             File fileValue = (File) value;
             if (!fileValue.exists()) {
-                throw new InvalidUserDataException(String.format(
-                        "File '%s' specified for property '%s' does not exist.", fileValue, propertyName));
-            }
-            if (!fileValue.isFile()) {
-                throw new InvalidUserDataException(String.format("File '%s' specified for property '%s' is not a file.",
-                        fileValue, propertyName));
+                messages.add(String.format("File '%s' specified for property '%s' does not exist.", fileValue, propertyName));
+            } else if (!fileValue.isFile()) {
+                messages.add(String.format("File '%s' specified for property '%s' is not a file.", fileValue, propertyName));
             }
         }
     };
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputFilesPropertyAnnotationHandler.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputFilesPropertyAnnotationHandler.java
index 14bc0f7..148c481 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputFilesPropertyAnnotationHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputFilesPropertyAnnotationHandler.java
@@ -15,9 +15,7 @@
  */
 package org.gradle.api.internal.project.taskfactory;
 
-import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.Task;
-import org.gradle.api.file.FileCollection;
 import org.gradle.api.tasks.InputFiles;
 import org.gradle.api.tasks.SkipWhenEmpty;
 
@@ -25,25 +23,19 @@ import java.lang.annotation.Annotation;
 import java.util.concurrent.Callable;
 
 public class InputFilesPropertyAnnotationHandler implements PropertyAnnotationHandler {
-    private final ValidationAction skipEmptyFileCollection = new ValidationAction() {
-        public void validate(String propertyName, Object value) throws InvalidUserDataException {
-            if (value instanceof FileCollection) {
-                ((FileCollection) value).stopExecutionIfEmpty();
-            }
-        }
-    };
-
     public Class<? extends Annotation> getAnnotationType() {
         return InputFiles.class;
     }
 
     public void attachActions(PropertyActionContext context) {
-        if (context.getTarget().getAnnotation(SkipWhenEmpty.class) != null) {
-            context.setSkipAction(skipEmptyFileCollection);
-        }
+        final boolean isSourceFiles = context.getTarget().getAnnotation(SkipWhenEmpty.class) != null;
         context.setConfigureAction(new UpdateAction() {
             public void update(Task task, Callable<Object> futureValue) {
-                task.getInputs().files(futureValue);
+                if (isSourceFiles) {
+                    task.getInputs().source(futureValue);
+                } else {
+                    task.getInputs().files(futureValue);
+                }
             }
         });
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/OutputDirectoryPropertyAnnotationHandler.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/OutputDirectoryPropertyAnnotationHandler.java
index 6d6341f..0ec5adb 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/OutputDirectoryPropertyAnnotationHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/OutputDirectoryPropertyAnnotationHandler.java
@@ -15,21 +15,32 @@
  */
 package org.gradle.api.internal.project.taskfactory;
 
+import org.gradle.api.Action;
 import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.Task;
 import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.util.GFileUtils;
+import org.gradle.util.UncheckedException;
 
 import java.io.File;
 import java.lang.annotation.Annotation;
+import java.util.Collection;
 import java.util.concurrent.Callable;
 
 public class OutputDirectoryPropertyAnnotationHandler implements PropertyAnnotationHandler {
     private final ValidationAction outputDirValidation = new ValidationAction() {
-        public void validate(String propertyName, Object value) throws InvalidUserDataException {
+        public void validate(String propertyName, Object value, Collection<String> messages) {
             File fileValue = (File) value;
-            if (!fileValue.isDirectory() && !fileValue.mkdirs()) {
-                throw new InvalidUserDataException(String.format(
-                        "Cannot create directory '%s' specified for property '%s'.", fileValue, propertyName));
+            if (fileValue.exists() && !fileValue.isDirectory()) {
+                messages.add(String.format("Directory '%s' specified for property '%s' is not a directory.", fileValue, propertyName));
+                return;
+            }
+
+            for (File candidate = fileValue; candidate != null && !candidate.isDirectory(); candidate = candidate.getParentFile()) {
+                if (candidate.exists() && !candidate.isDirectory()) {
+                    messages.add(String.format("Cannot write to directory '%s' specified for property '%s', as ancestor '%s' is not a directory.", fileValue, propertyName, candidate));
+                    break;
+                }
             }
         }
     };
@@ -38,11 +49,29 @@ public class OutputDirectoryPropertyAnnotationHandler implements PropertyAnnotat
         return OutputDirectory.class;
     }
 
-    public void attachActions(PropertyActionContext context) {
+    public void attachActions(final PropertyActionContext context) {
         context.setValidationAction(outputDirValidation);
         context.setConfigureAction(new UpdateAction() {
-            public void update(Task task, Callable<Object> futureValue) {
+            public void update(Task task, final Callable<Object> futureValue) {
                 task.getOutputs().files(futureValue);
+                task.doFirst(new Action<Task>() {
+                    public void execute(Task task) {
+                        File fileValue;
+                        try {
+                            fileValue = (File) futureValue.call();
+                        } catch (Exception e) {
+                            throw UncheckedException.asUncheckedException(e);
+                        }
+                        if (fileValue == null) {
+                            return;
+                        }
+                        fileValue = GFileUtils.canonicalise(fileValue);
+                        if (!fileValue.isDirectory() && !fileValue.mkdirs()) {
+                            throw new InvalidUserDataException(String.format(
+                                    "Cannot create directory '%s' specified for property '%s'.", fileValue, context.getName()));
+                        }
+                    }
+                });
             }
         });
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/OutputFilePropertyAnnotationHandler.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/OutputFilePropertyAnnotationHandler.java
index 29a3f82..0ca9b6d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/OutputFilePropertyAnnotationHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/OutputFilePropertyAnnotationHandler.java
@@ -15,28 +15,31 @@
  */
 package org.gradle.api.internal.project.taskfactory;
 
+import org.gradle.api.Action;
 import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.Task;
 import org.gradle.api.tasks.OutputFile;
 import org.gradle.util.GFileUtils;
+import org.gradle.util.UncheckedException;
 
 import java.io.File;
 import java.lang.annotation.Annotation;
+import java.util.Collection;
 import java.util.concurrent.Callable;
 
 public class OutputFilePropertyAnnotationHandler implements PropertyAnnotationHandler {
     private final ValidationAction ouputFileValidation = new ValidationAction() {
-        public void validate(String propertyName, Object value) throws InvalidUserDataException {
+        public void validate(String propertyName, Object value, Collection<String> messages) {
             File fileValue = GFileUtils.canonicalise((File) value);
-            if (fileValue.exists() && !fileValue.isFile()) {
-                throw new InvalidUserDataException(String.format(
-                        "Cannot write to file '%s' specified for property '%s' as it is a directory.", fileValue,
-                        propertyName));
+            if (fileValue.exists() && fileValue.isDirectory()) {
+                messages.add(String.format("Cannot write to file '%s' specified for property '%s' as it is a directory.", fileValue, propertyName));
             }
-            if (!fileValue.getParentFile().isDirectory() && !fileValue.getParentFile().mkdirs()) {
-                throw new InvalidUserDataException(String.format(
-                        "Cannot create parent directory '%s' of file specified for property '%s'.",
-                        fileValue.getParentFile(), propertyName));
+            
+            for (File candidate = fileValue.getParentFile(); candidate != null && !candidate.isDirectory(); candidate = candidate.getParentFile()) {
+                if (candidate.exists() && !candidate.isDirectory()) {
+                    messages.add(String.format("Cannot write to file '%s' specified for property '%s', as ancestor '%s' is not a directory.", fileValue, propertyName, candidate));
+                    break;
+                }
             }
         }
     };
@@ -45,11 +48,28 @@ public class OutputFilePropertyAnnotationHandler implements PropertyAnnotationHa
         return OutputFile.class;
     }
 
-    public void attachActions(PropertyActionContext context) {
+    public void attachActions(final PropertyActionContext context) {
         context.setValidationAction(ouputFileValidation);
         context.setConfigureAction(new UpdateAction() {
-            public void update(Task task, Callable<Object> futureValue) {
+            public void update(Task task, final Callable<Object> futureValue) {
                 task.getOutputs().files(futureValue);
+                task.doFirst(new Action<Task>() {
+                    public void execute(Task task) {
+                        File fileValue;
+                        try {
+                            fileValue = (File) futureValue.call();
+                        } catch (Exception e) {
+                            throw UncheckedException.asUncheckedException(e);
+                        }
+                        if (fileValue == null) {
+                            return;
+                        }
+                        fileValue = GFileUtils.canonicalise(fileValue);
+                        if (!fileValue.getParentFile().isDirectory() && !fileValue.getParentFile().mkdirs()) {
+                            throw new InvalidUserDataException(String.format("Cannot create parent directory '%s' of file specified for property '%s'.", fileValue.getParentFile(), context.getName()));
+                        }
+                    }
+                });
             }
         });
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/PostExecutionAnalysisTaskExecuter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/PostExecutionAnalysisTaskExecuter.java
deleted file mode 100644
index 4794ff7..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/PostExecutionAnalysisTaskExecuter.java
+++ /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.internal.project.taskfactory;
-
-import org.gradle.api.Task;
-import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.internal.tasks.TaskExecuter;
-import org.gradle.api.internal.tasks.TaskStateInternal;
-
-public class PostExecutionAnalysisTaskExecuter implements TaskExecuter {
-    private final TaskExecuter executer;
-
-    public PostExecutionAnalysisTaskExecuter(TaskExecuter executer) {
-        this.executer = executer;
-    }
-
-    public void execute(TaskInternal task, TaskStateInternal state) {
-        executer.execute(task, state);
-        if (task.getActions().isEmpty()) {
-            boolean upToDate = true;
-            for (Task dependency : task.getTaskDependencies().getDependencies(task)) {
-                if (!dependency.getState().getSkipped()) {
-                    upToDate = false;
-                    break;
-                }
-            }
-            if (upToDate) {
-                state.upToDate();
-            }
-        } else if (!state.getDidWork()) {
-            state.upToDate();
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/PropertyActionContext.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/PropertyActionContext.java
index f912dfb..296a8a0 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/PropertyActionContext.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/PropertyActionContext.java
@@ -40,12 +40,6 @@ public interface PropertyActionContext {
     void setValidationAction(ValidationAction action);
 
     /**
-     * Specifies the action used to skip the task based on the value of this property. Note that this action is called
-     * before the validation action.
-     */
-    void setSkipAction(ValidationAction action);
-
-    /**
      * Specified the action used to configure the task based on the value of this property. Note that this action is
      * called before the skip and validation actions.
      */
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/TaskFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/TaskFactory.java
index 957afc5..10368f5 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/TaskFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/TaskFactory.java
@@ -62,6 +62,10 @@ public class TaskFactory implements ITaskFactory {
         if (description != null) {
             task.setDescription(description.toString());
         }
+        Object group = actualArgs.get(Task.TASK_GROUP);
+        if (group != null) {
+            task.setGroup(group.toString());
+        }
         Object action = actualArgs.get(Task.TASK_ACTION);
         if (action instanceof Action) {
             Action<? super Task> taskAction = (Action<? super Task>) action;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/ValidationAction.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/ValidationAction.java
index b7cd4f1..4c8f4ac 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/ValidationAction.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/ValidationAction.java
@@ -15,8 +15,8 @@
  */
 package org.gradle.api.internal.project.taskfactory;
 
-import org.gradle.api.InvalidUserDataException;
+import java.util.Collection;
 
 interface ValidationAction {
-    void validate(String propertyName, Object value) throws InvalidUserDataException;
+    void validate(String propertyName, Object value, Collection<String> messages);
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskExecuter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskExecuter.java
deleted file mode 100644
index adb3e5a..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskExecuter.java
+++ /dev/null
@@ -1,73 +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;
-
-import org.gradle.api.Action;
-import org.gradle.api.GradleException;
-import org.gradle.api.Task;
-import org.gradle.api.execution.TaskActionListener;
-import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-import org.gradle.api.tasks.StopActionException;
-import org.gradle.api.tasks.StopExecutionException;
-import org.gradle.api.tasks.TaskExecutionException;
-
-public class DefaultTaskExecuter implements TaskExecuter {
-    private static Logger logger = Logging.getLogger(DefaultTaskExecuter.class);
-    private final TaskActionListener listener;
-
-    public DefaultTaskExecuter(TaskActionListener listener) {
-        this.listener = listener;
-    }
-
-    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);
-        }
-    }
-
-    private GradleException executeActions(TaskInternal task, TaskStateInternal state) {
-        logger.debug("Executing actions for {}.", task);
-        for (Action<? super Task> action : task.getActions()) {
-            state.setDidWork(true);
-            task.getStandardOutputCapture().start();
-            try {
-                action.execute(task);
-            } catch (StopActionException e) {
-                // Ignore
-                logger.debug("Action stopped by some action with message: {}", e.getMessage());
-            } catch (StopExecutionException e) {
-                logger.info("Execution stopped by some action with message: {}", e.getMessage());
-                break;
-            } catch (Throwable t) {
-                return new TaskExecutionException(task, t);
-            } finally {
-                task.getStandardOutputCapture().stop();
-            }
-        }
-        return null;
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskInputs.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskInputs.java
index 0a9f141..841becc 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskInputs.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskInputs.java
@@ -17,8 +17,10 @@ package org.gradle.api.internal.tasks;
 
 import groovy.lang.Closure;
 import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.TaskInternal;
 import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.internal.file.PathResolvingFileCollection;
+import org.gradle.api.internal.file.UnionFileCollection;
+import org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection;
 import org.gradle.api.tasks.TaskInputs;
 import org.gradle.util.UncheckedException;
 
@@ -27,21 +29,23 @@ import java.util.Map;
 import java.util.concurrent.Callable;
 
 public class DefaultTaskInputs implements TaskInputs {
-    private final PathResolvingFileCollection inputFiles;
+    private final DefaultConfigurableFileCollection inputFiles;
+    private final DefaultConfigurableFileCollection sourceFiles;
     private final FileResolver resolver;
     private final Map<String, Object> properties = new HashMap<String, Object>();
 
-    public DefaultTaskInputs(FileResolver resolver) {
+    public DefaultTaskInputs(FileResolver resolver, TaskInternal task) {
         this.resolver = resolver;
-        inputFiles = new PathResolvingFileCollection("task input files", resolver, null);
+        inputFiles = new DefaultConfigurableFileCollection(String.format("%s input files", task), resolver, null);
+        sourceFiles = new DefaultConfigurableFileCollection(String.format("%s source files", task), resolver, null);
     }
 
     public boolean getHasInputs() {
-        return !inputFiles.getSources().isEmpty() || !properties.isEmpty();
+        return !inputFiles.getFrom().isEmpty() || !properties.isEmpty() || !sourceFiles.getFrom().isEmpty();
     }
 
     public FileCollection getFiles() {
-        return inputFiles;
+        return new UnionFileCollection(inputFiles, sourceFiles);
     }
 
     public TaskInputs files(Object... paths) {
@@ -59,6 +63,29 @@ public class DefaultTaskInputs implements TaskInputs {
         return this;
     }
 
+    public boolean getHasSourceFiles() {
+        return !sourceFiles.getFrom().isEmpty();
+    }
+
+    public FileCollection getSourceFiles() {
+        return sourceFiles;
+    }
+
+    public TaskInputs source(Object... paths) {
+        sourceFiles.from(paths);
+        return this;
+    }
+
+    public TaskInputs source(Object path) {
+        sourceFiles.from(path);
+        return this;
+    }
+
+    public TaskInputs sourceDir(Object path) {
+        sourceFiles.from(resolver.resolveFilesAsTree(path));
+        return this;
+    }
+
     public Map<String, Object> getProperties() {
         Map<String, Object> actualProperties = new HashMap<String, Object>();
         for (Map.Entry<String, Object> entry : properties.entrySet()) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskOutputs.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskOutputs.java
index 5d1f65c..59d9230 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskOutputs.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskOutputs.java
@@ -23,18 +23,18 @@ import org.gradle.api.internal.TaskExecutionHistory;
 import org.gradle.api.internal.TaskInternal;
 import org.gradle.api.internal.TaskOutputsInternal;
 import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.internal.file.PathResolvingFileCollection;
+import org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection;
 import org.gradle.api.specs.AndSpec;
 import org.gradle.api.specs.Spec;
 import org.gradle.api.tasks.TaskOutputs;
 
 public class DefaultTaskOutputs implements TaskOutputsInternal {
-    private final PathResolvingFileCollection outputFiles;
+    private final DefaultConfigurableFileCollection outputFiles;
     private AndSpec<TaskInternal> upToDateSpec = new AndSpec<TaskInternal>();
     private TaskExecutionHistory history;
 
     public DefaultTaskOutputs(FileResolver resolver, TaskInternal task) {
-        outputFiles = new PathResolvingFileCollection("task output files", resolver, null);
+        outputFiles = new DefaultConfigurableFileCollection(String.format("%s output files", task), resolver, null);
         outputFiles.builtBy(task);
     }
 
@@ -51,7 +51,7 @@ public class DefaultTaskOutputs implements TaskOutputsInternal {
     }
 
     public boolean getHasOutput() {
-        return !outputFiles.getSources().isEmpty() || !upToDateSpec.getSpecs().isEmpty();
+        return !outputFiles.getFrom().isEmpty() || !upToDateSpec.getSpecs().isEmpty();
     }
 
     public FileCollection getFiles() {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/ExecuteAtMostOnceTaskExecuter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/ExecuteAtMostOnceTaskExecuter.java
deleted file mode 100644
index 512b8ff..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/ExecuteAtMostOnceTaskExecuter.java
+++ /dev/null
@@ -1,34 +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;
-
-import org.gradle.api.internal.TaskInternal;
-
-public class ExecuteAtMostOnceTaskExecuter implements TaskExecuter {
-    private final TaskExecuter executer;
-
-    public ExecuteAtMostOnceTaskExecuter(TaskExecuter executer) {
-        this.executer = executer;
-    }
-
-    public void execute(TaskInternal task, TaskStateInternal state) {
-        if (state.getExecuted()) {
-            return;
-        }
-        executer.execute(task, state);
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/SkipTaskExecuter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/SkipTaskExecuter.java
deleted file mode 100644
index 9b7a512..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/SkipTaskExecuter.java
+++ /dev/null
@@ -1,59 +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;
-
-import org.gradle.api.GradleException;
-import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-
-public class SkipTaskExecuter implements TaskExecuter {
-    private static Logger logger = Logging.getLogger(SkipTaskExecuter.class);
-    private final TaskExecuter executer;
-
-    public SkipTaskExecuter(TaskExecuter executer) {
-        this.executer = executer;
-    }
-
-    public void execute(TaskInternal task, TaskStateInternal state) {
-        logger.debug("Starting to execute {}", task);
-        try {
-            doExecute(task, state);
-        } finally {
-            state.executed();
-            logger.debug("Finished executing {}", task);
-        }
-    }
-
-    private void doExecute(TaskInternal task, TaskStateInternal state) {
-        boolean skip;
-        try {
-            skip = !task.getOnlyIf().isSatisfiedBy(task);
-        } catch (Throwable t) {
-            state.executed(new GradleException(String.format("Could not evaluate onlyIf predicate for %s.", task), t));
-            return;
-        }
-
-        if (skip) {
-            logger.info("Skipping execution as task onlyIf is false.");
-            state.skipped("SKIPPED");
-            return;
-        }
-
-        executer.execute(task, state);
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskStateInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskStateInternal.java
index 2047dbc..4033bff 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskStateInternal.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskStateInternal.java
@@ -46,14 +46,14 @@ public class TaskStateInternal implements TaskState {
     }
 
     /**
-     * Marks this task as executed with no failure.
+     * Marks this task as executed. This method can be called multiple times.
      */
     public void executed() {
         this.executed = true;
     }
 
     /**
-     * Marks this task as executed with the given failure.
+     * Marks this task as executed with the given failure. This method can be called at most once.
      */
     public void executed(Throwable failure) {
         assert this.failure == null;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/ExecuteActionsTaskExecuter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/ExecuteActionsTaskExecuter.java
new file mode 100644
index 0000000..7819b6a
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/ExecuteActionsTaskExecuter.java
@@ -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.api.internal.tasks.execution;
+
+import org.gradle.api.Action;
+import org.gradle.api.GradleException;
+import org.gradle.api.Task;
+import org.gradle.api.execution.TaskActionListener;
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.tasks.TaskExecuter;
+import org.gradle.api.internal.tasks.TaskStateInternal;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.api.tasks.StopActionException;
+import org.gradle.api.tasks.StopExecutionException;
+import org.gradle.api.tasks.TaskExecutionException;
+
+/**
+ * A {@link org.gradle.api.internal.tasks.TaskExecuter} which executes the actions of a task.
+ */
+public class ExecuteActionsTaskExecuter implements TaskExecuter {
+    private static Logger logger = Logging.getLogger(ExecuteActionsTaskExecuter.class);
+    private final TaskActionListener listener;
+
+    public ExecuteActionsTaskExecuter(TaskActionListener listener) {
+        this.listener = listener;
+    }
+
+    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);
+        }
+    }
+
+    private GradleException executeActions(TaskInternal task, TaskStateInternal state) {
+        logger.debug("Executing actions for {}.", task);
+        for (Action<? super Task> action : task.getActions()) {
+            state.setDidWork(true);
+            task.getStandardOutputCapture().start();
+            try {
+                action.execute(task);
+            } catch (StopActionException e) {
+                // Ignore
+                logger.debug("Action stopped by some action with message: {}", e.getMessage());
+            } catch (StopExecutionException e) {
+                logger.info("Execution stopped by some action with message: {}", e.getMessage());
+                break;
+            } catch (Throwable t) {
+                return new TaskExecutionException(task, t);
+            } finally {
+                task.getStandardOutputCapture().stop();
+            }
+        }
+        return null;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/ExecuteAtMostOnceTaskExecuter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/ExecuteAtMostOnceTaskExecuter.java
new file mode 100644
index 0000000..9744ee5
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/ExecuteAtMostOnceTaskExecuter.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.execution;
+
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.tasks.TaskExecuter;
+import org.gradle.api.internal.tasks.TaskStateInternal;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+
+/**
+ * A {@link org.gradle.api.internal.tasks.TaskExecuter} which will execute a task once only.
+ */
+public class ExecuteAtMostOnceTaskExecuter implements TaskExecuter {
+    private static final Logger LOGGER = Logging.getLogger(ExecuteAtMostOnceTaskExecuter.class);
+    private final TaskExecuter executer;
+
+    public ExecuteAtMostOnceTaskExecuter(TaskExecuter executer) {
+        this.executer = executer;
+    }
+
+    public void execute(TaskInternal task, TaskStateInternal state) {
+        if (state.getExecuted()) {
+            return;
+        }
+        LOGGER.debug("Starting to execute {}", task);
+        try {
+            executer.execute(task, state);
+        } finally {
+            state.executed();
+            LOGGER.debug("Finished executing {}", task);
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/PostExecutionAnalysisTaskExecuter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/PostExecutionAnalysisTaskExecuter.java
new file mode 100644
index 0000000..e866788
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/PostExecutionAnalysisTaskExecuter.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.api.internal.tasks.execution;
+
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.tasks.TaskExecuter;
+import org.gradle.api.internal.tasks.TaskStateInternal;
+
+/**
+ * A {@link TaskExecuter} which marks tasks as up-to-date if they did no work.
+ */
+public class PostExecutionAnalysisTaskExecuter implements TaskExecuter {
+    private final TaskExecuter executer;
+
+    public PostExecutionAnalysisTaskExecuter(TaskExecuter executer) {
+        this.executer = executer;
+    }
+
+    public void execute(TaskInternal task, TaskStateInternal state) {
+        executer.execute(task, state);
+        if (!state.getDidWork()) {
+            state.upToDate();
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/SkipEmptySourceFilesTaskExecuter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/SkipEmptySourceFilesTaskExecuter.java
new file mode 100644
index 0000000..e847392
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/SkipEmptySourceFilesTaskExecuter.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.execution;
+
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.tasks.TaskExecuter;
+import org.gradle.api.internal.tasks.TaskStateInternal;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+
+/**
+ * A {@link TaskExecuter} which skips tasks whose source file collection is empty.
+ */
+public class SkipEmptySourceFilesTaskExecuter implements TaskExecuter {
+    private static final Logger LOGGER = Logging.getLogger(SkipEmptySourceFilesTaskExecuter.class);
+    private final TaskExecuter executer;
+
+    public SkipEmptySourceFilesTaskExecuter(TaskExecuter executer) {
+        this.executer = executer;
+    }
+
+    public void execute(TaskInternal task, TaskStateInternal state) {
+        if (task.getInputs().getHasSourceFiles() && task.getInputs().getSourceFiles().isEmpty()) {
+            LOGGER.info("Skipping {} as it has no source files.", task);
+            state.upToDate();
+            return;
+        }
+        executer.execute(task, state);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/SkipOnlyIfTaskExecuter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/SkipOnlyIfTaskExecuter.java
new file mode 100644
index 0000000..e56584d
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/SkipOnlyIfTaskExecuter.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.api.internal.tasks.execution;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.tasks.TaskExecuter;
+import org.gradle.api.internal.tasks.TaskStateInternal;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+
+/**
+ * A {@link org.gradle.api.internal.tasks.TaskExecuter} which skips tasks whose onlyIf predicate evaluates to false
+ */
+public class SkipOnlyIfTaskExecuter implements TaskExecuter {
+    private static final Logger LOGGER = Logging.getLogger(SkipOnlyIfTaskExecuter.class);
+    private final TaskExecuter executer;
+
+    public SkipOnlyIfTaskExecuter(TaskExecuter executer) {
+        this.executer = executer;
+    }
+
+    public void execute(TaskInternal task, TaskStateInternal state) {
+        boolean skip;
+        try {
+            skip = !task.getOnlyIf().isSatisfiedBy(task);
+        } catch (Throwable t) {
+            state.executed(new GradleException(String.format("Could not evaluate onlyIf predicate for %s.", task), t));
+            return;
+        }
+
+        if (skip) {
+            LOGGER.info("Skipping {} as task onlyIf is false.", task);
+            state.skipped("SKIPPED");
+            return;
+        }
+
+        executer.execute(task, state);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/SkipTaskWithNoActionsExecuter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/SkipTaskWithNoActionsExecuter.java
new file mode 100644
index 0000000..088b2b6
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/SkipTaskWithNoActionsExecuter.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.execution;
+
+import org.gradle.api.Task;
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.tasks.TaskExecuter;
+import org.gradle.api.internal.tasks.TaskStateInternal;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+
+/**
+ * A {@link org.gradle.api.internal.tasks.TaskExecuter} which skips tasks that have no actions.
+ */
+public class SkipTaskWithNoActionsExecuter implements TaskExecuter {
+    private static final Logger LOGGER = Logging.getLogger(SkipTaskWithNoActionsExecuter.class);
+    private final TaskExecuter executer;
+
+    public SkipTaskWithNoActionsExecuter(TaskExecuter executer) {
+        this.executer = executer;
+    }
+
+    public void execute(TaskInternal task, TaskStateInternal state) {
+        if (task.getActions().isEmpty()) {
+            LOGGER.info("Skipping {} as it has no actions.", task);
+            boolean upToDate = true;
+            for (Task dependency : task.getTaskDependencies().getDependencies(task)) {
+                if (!dependency.getState().getSkipped()) {
+                    upToDate = false;
+                    break;
+                }
+            }
+            if (upToDate) {
+                state.upToDate();
+            }
+            return;
+        }
+        executer.execute(task, state);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/SkipUpToDateTaskExecuter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/SkipUpToDateTaskExecuter.java
new file mode 100644
index 0000000..e478e61
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/SkipUpToDateTaskExecuter.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.api.internal.tasks.execution;
+
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.changedetection.TaskArtifactState;
+import org.gradle.api.internal.changedetection.TaskArtifactStateRepository;
+import org.gradle.api.internal.tasks.TaskExecuter;
+import org.gradle.api.internal.tasks.TaskStateInternal;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A {@link TaskExecuter} which skips tasks whose outputs are up-to-date.
+ */
+public class SkipUpToDateTaskExecuter implements TaskExecuter {
+    private static final Logger LOGGER = LoggerFactory.getLogger(SkipUpToDateTaskExecuter.class);
+    private final TaskExecuter executer;
+    private final TaskArtifactStateRepository repository;
+
+    public SkipUpToDateTaskExecuter(TaskExecuter executer, TaskArtifactStateRepository repository) {
+        this.executer = executer;
+        this.repository = repository;
+    }
+
+    public void execute(TaskInternal task, TaskStateInternal state) {
+        LOGGER.debug("Determining if {} is up-to-date", task);
+        TaskArtifactState taskArtifactState = repository.getStateFor(task);
+        try {
+            if (taskArtifactState.isUpToDate()) {
+                LOGGER.info("Skipping {} as it is up-to-date", task);
+                state.upToDate();
+                return;
+
+            }
+            LOGGER.debug("{} is not up-to-date", task);
+
+            taskArtifactState.beforeTask();
+            task.getOutputs().setHistory(taskArtifactState.getExecutionHistory());
+            try {
+                executer.execute(task, state);
+                if (state.getFailure() == null) {
+                    taskArtifactState.afterTask();
+                }
+            } finally {
+                task.getOutputs().setHistory(null);
+            }
+        } finally {
+            taskArtifactState.finished();
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/TaskValidator.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/TaskValidator.java
new file mode 100644
index 0000000..55b0166
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/TaskValidator.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.execution;
+
+import org.gradle.api.internal.TaskInternal;
+
+import java.util.Collection;
+
+public interface TaskValidator {
+    /**
+     * Validates this task, adding violations to the given collections.
+     */
+    void validate(TaskInternal task, Collection<String> messages);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/ValidatingTaskExecuter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/ValidatingTaskExecuter.java
new file mode 100644
index 0000000..67927c2
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/ValidatingTaskExecuter.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.execution;
+
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.tasks.TaskExecuter;
+import org.gradle.api.internal.tasks.TaskStateInternal;
+import org.gradle.api.tasks.TaskValidationException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A {@link TaskExecuter} which performs validation before executing the task.
+ */
+public class ValidatingTaskExecuter implements TaskExecuter {
+    private final TaskExecuter executer;
+
+    public ValidatingTaskExecuter(TaskExecuter executer) {
+        this.executer = executer;
+    }
+
+    public void execute(TaskInternal task, TaskStateInternal state) {
+        List<String> messages = new ArrayList<String>();
+        for (TaskValidator validator : task.getValidators()) {
+            validator.validate(task, messages);
+        }
+        if (!messages.isEmpty()) {
+            List<InvalidUserDataException> causes = new ArrayList<InvalidUserDataException>();
+            messages = messages.subList(0, Math.min(5, messages.size()));
+            for (String message : messages) {
+                causes.add(new InvalidUserDataException(message));
+            }
+            String errorMessage;
+            if (messages.size() == 1) {
+                errorMessage = String.format("A problem was found with the configuration of %s.", task);
+            } else {
+                errorMessage = String.format("Some problems were found with the configuration of %s.", task);
+            }
+            state.executed(new TaskValidationException(errorMessage, causes));
+            return;
+        }
+        executer.execute(task, state);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/generator/AbstractPersistableConfigurationObject.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/generator/AbstractPersistableConfigurationObject.java
deleted file mode 100644
index 5858b10..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/generator/AbstractPersistableConfigurationObject.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.api.internal.tasks.generator;
-
-import org.gradle.util.UncheckedException;
-
-import java.io.*;
-
-public abstract class AbstractPersistableConfigurationObject implements PersistableConfigurationObject {
-    public void load(File inputFile) {
-        try {
-            InputStream inputStream = new FileInputStream(inputFile);
-            try {
-                load(inputStream);
-            } finally {
-                inputStream.close();
-            }
-        } catch (Exception e) {
-            throw UncheckedException.asUncheckedException(e);
-        }
-    }
-
-    public void loadDefaults() {
-        try {
-            InputStream inputStream = getClass().getResourceAsStream(getDefaultResourceName());
-            try {
-                load(inputStream);
-            } finally {
-                inputStream.close();
-            }
-        } catch (Exception e) {
-            throw UncheckedException.asUncheckedException(e);
-        }
-    }
-
-    public abstract void load(InputStream inputStream) throws Exception;
-
-    public void store(File outputFile) {
-        try {
-            OutputStream outputStream = new FileOutputStream(outputFile);
-            try {
-                store(outputStream);
-            } finally {
-                outputStream.close();
-            }
-        } catch (IOException e) {
-            throw UncheckedException.asUncheckedException(e);
-        }
-    }
-
-    public abstract void store(OutputStream outputStream);
-
-    protected abstract String getDefaultResourceName();
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/generator/Generator.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/generator/Generator.java
deleted file mode 100644
index c748703..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/generator/Generator.java
+++ /dev/null
@@ -1,32 +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.generator;
-
-import java.io.File;
-
-/**
- * Responsible for reading, configuring and writing a config object of type T to/from a file.
- * @param <T>
- */
-public interface Generator<T> {
-    T read(File inputFile);
-
-    T defaultInstance();
-
-    void configure(T object);
-
-    void write(T object, File outputFile);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/generator/PersistableConfigurationObject.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/generator/PersistableConfigurationObject.java
deleted file mode 100644
index e87524b..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/generator/PersistableConfigurationObject.java
+++ /dev/null
@@ -1,26 +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.generator;
-
-import java.io.File;
-
-public interface PersistableConfigurationObject {
-    void load(File inputFile);
-
-    void loadDefaults();
-
-    void store(File outputFile);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/generator/PersistableConfigurationObjectGenerator.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/generator/PersistableConfigurationObjectGenerator.java
deleted file mode 100644
index 7d5efab..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/generator/PersistableConfigurationObjectGenerator.java
+++ /dev/null
@@ -1,44 +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.generator;
-
-import org.gradle.api.internal.Factory;
-
-import java.io.File;
-
-/**
- * Adapts a {@link PersistableConfigurationObject} to a {@link
- * Generator}.
- *
- * @param <T> the configuration object type.
- */
-public abstract class PersistableConfigurationObjectGenerator<T extends PersistableConfigurationObject> implements Generator<T>, Factory<T> {
-    public T read(File inputFile) {
-        T obj = create();
-        obj.load(inputFile);
-        return obj;
-    }
-
-    public T defaultInstance() {
-        T obj = create();
-        obj.loadDefaults();
-        return obj;
-    }
-
-    public void write(T object, File outputFile) {
-        object.store(outputFile);
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/generator/PropertiesPersistableConfigurationObject.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/generator/PropertiesPersistableConfigurationObject.java
deleted file mode 100644
index 7d66ac3..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/generator/PropertiesPersistableConfigurationObject.java
+++ /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.internal.tasks.generator;
-
-import org.gradle.util.UncheckedException;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Properties;
-
-public abstract class PropertiesPersistableConfigurationObject extends AbstractPersistableConfigurationObject {
-    private Properties properties;
-
-    @Override
-    public void load(InputStream inputStream) throws Exception {
-        properties = new Properties();
-        properties.load(inputStream);
-        load(properties);
-    }
-
-    @Override
-    public void store(OutputStream outputStream) {
-        store(properties);
-        try {
-            properties.store(outputStream, "");
-        } catch (IOException e) {
-            throw UncheckedException.asUncheckedException(e);
-        }
-    }
-
-    protected abstract void store(Properties properties);
-
-    protected abstract void load(Properties properties);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/generator/XmlPersistableConfigurationObject.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/generator/XmlPersistableConfigurationObject.java
deleted file mode 100644
index d86f779..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/generator/XmlPersistableConfigurationObject.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.api.internal.tasks.generator;
-
-import groovy.util.Node;
-import groovy.util.XmlParser;
-import org.gradle.api.internal.XmlTransformer;
-
-import java.io.*;
-
-/**
- * A {@link PersistableConfigurationObject} which is stored in an XML file.
- */
-public abstract class XmlPersistableConfigurationObject extends AbstractPersistableConfigurationObject {
-    private final XmlTransformer xmlTransformer;
-    private Node xml;
-
-    protected XmlPersistableConfigurationObject(XmlTransformer xmlTransformer) {
-        this.xmlTransformer = xmlTransformer;
-    }
-
-    public Node getXml() {
-        return xml;
-    }
-
-    @Override
-    public void load(InputStream inputStream) throws Exception {
-        xml = new XmlParser().parse(inputStream);
-        load(xml);
-    }
-
-    @Override
-    public void store(OutputStream outputStream) {
-        store(xml);
-        xmlTransformer.transform(xml, outputStream);
-    }
-
-    /**
-     * Called immediately after the XML file has been read.
-     */
-    protected abstract void load(Node xml);
-
-    /**
-     * Called immediately before the XML file is to be written.
-     */
-    protected abstract void store(Node xml);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/invocation/Gradle.java b/subprojects/core/src/main/groovy/org/gradle/api/invocation/Gradle.java
index 43afa2d..4b5ab95 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/invocation/Gradle.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/invocation/Gradle.java
@@ -49,7 +49,6 @@ public interface Gradle {
      * executing this build.</p>
      *
      * @return The home directory. May return null.
-     * @deprecated No replacement
      */
     File getGradleHomeDir();
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/logging/LoggingManager.java b/subprojects/core/src/main/groovy/org/gradle/api/logging/LoggingManager.java
index 24f6266..f1b70ab 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/logging/LoggingManager.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/logging/LoggingManager.java
@@ -70,6 +70,13 @@ public interface LoggingManager extends LoggingOutput {
     LogLevel getStandardErrorCaptureLevel();
 
     /**
+     * Returns the current logging level.
+     *
+     * @return The current logging level.
+     */
+    LogLevel getLevel();
+
+    /**
      * Sets the minimum logging level. All messages at a lower level are discarded.
      *
      * @param logLevel The minimum logging level.
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/AbstractCopyTask.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/AbstractCopyTask.java
index 0fed181..45ba32f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/AbstractCopyTask.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/AbstractCopyTask.java
@@ -94,6 +94,20 @@ public abstract class AbstractCopyTask extends ConventionTask implements CopySpe
     /**
      * {@inheritDoc}
      */
+    public boolean getIncludeEmptyDirs() {
+        return getMainSpec().getIncludeEmptyDirs();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setIncludeEmptyDirs(boolean includeEmptyDirs) {
+        getMainSpec().setIncludeEmptyDirs(includeEmptyDirs);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
     public AbstractCopyTask from(Object... sourcePaths) {
         getMainSpec().from(sourcePaths);
         return this;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/Delete.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/Delete.java
index 7794109..d2a1177 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/Delete.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/Delete.java
@@ -23,7 +23,12 @@ import java.util.LinkedHashSet;
 import java.util.Set;
 
 /**
- * <p>Deletes files or directories.</p>
+ * <p>Deletes files or directories. Example:</p>
+ * <pre autoTested=''>
+ * task makePretty(type: Delete) {
+ *   delete 'uglyFolder', 'uglyFile'
+ * }
+ * </pre>
  *
  * @author Hans Dockter
  */
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/Exec.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/Exec.java
index 812c910..d8637dd6 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/Exec.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/Exec.java
@@ -30,7 +30,18 @@ import java.util.List;
 import java.util.Map;
 
 /**
- * Executes a command line process.
+ * Executes a command line process. Example:
+ * <pre autoTested=''>
+ * task stopTomcat(type:Exec) {
+ *   workingDir = file('../tomcat/bin')
+ *
+ *   //on windows:
+ *   commandLine = ['cmd', '/c', 'stop.bat']
+ *
+ *   //on linux (oh yeah!!!)
+ *   commandLine = ['./stop.sh']
+ * }
+ * </pre>
  * 
  * @author Hans Dockter
  */
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/GeneratorTask.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/GeneratorTask.java
deleted file mode 100644
index 7bee1e2..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/GeneratorTask.java
+++ /dev/null
@@ -1,163 +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;
-
-import groovy.lang.Closure;
-import org.gradle.api.Action;
-import org.gradle.api.internal.ConventionTask;
-import org.gradle.api.internal.tasks.generator.Generator;
-import org.gradle.api.specs.Specs;
-import org.gradle.listener.ActionBroadcast;
-
-import java.io.File;
-
-/**
- * <p>A {@code GeneratorTask} generates a configuration file based on a domain object of type T. When executed, the
- * task:</p>
- *
- * <ul>
- *
- * <li>loads the object from the input file, if it exists.</li>
- *
- * <li>Calls the beforeConfigured actions, passing the object to each action.</li>
- *
- * <li>Configures the object in some task-specific way.</li>
- *
- * <li>Calls the afterConfigured actions, passing the object to each action.</li>
- *
- * <li>writes the object to the output file.</li>
- *
- * </ul>
- *
- * @param <T> The domain object for the configuration file.
- */
-public class GeneratorTask<T> extends ConventionTask {
-    private File inputFile;
-    private File outputFile;
-    private final ActionBroadcast<T> beforeConfigured = new ActionBroadcast<T>();
-    private final ActionBroadcast<T> afterConfigured = new ActionBroadcast<T>();
-    protected Generator<T> generator;
-
-    public GeneratorTask() {
-        getOutputs().upToDateWhen(Specs.satisfyNone());
-    }
-
-    @TaskAction
-    void generate() {
-        File inputFile = getInputFile();
-        T object;
-        if (inputFile.exists()) {
-            object = generator.read(inputFile);
-        } else {
-            object = generator.defaultInstance();
-        }
-        beforeConfigured.execute(object);
-        generator.configure(object);
-        afterConfigured.execute(object);
-        generator.write(object, getOutputFile());
-    }
-
-    /**
-     * The input file to load the initial configuration from. Defaults to the output file. If the specified input file
-     * does not exist, this task uses some default initial configuration.
-     *
-     * @return The input file.
-     */
-    public File getInputFile() {
-        return inputFile != null ? inputFile : getOutputFile();
-    }
-
-    /**
-     * Sets the input file to load the initial configuration from.
-     *
-     * @param inputFile The input file. Use null to use the output file.
-     */
-    public void setInputFile(File inputFile) {
-        this.inputFile = inputFile;
-    }
-
-    /**
-     * The output file to write the final configuration to.
-     *
-     * @return The output file.
-     */
-    @OutputFile
-    public File getOutputFile() {
-        return outputFile;
-    }
-
-    /**
-     * Sets the output file to write the final configuration to.
-     *
-     * @param outputFile The output file.
-     */
-    public void setOutputFile(File outputFile) {
-        this.outputFile = outputFile;
-    }
-
-    /**
-     * <p>Adds a closure to be called before the domain object is configured by this task. The domain object is passed
-     * as a parameter to the closure.</p>
-     *
-     * <p>The closure is executed after the domain object has been loaded from the input file. Using this method allows
-     * you to change the domain object in some way before the task configures it.</p>
-     *
-     * @param closure The closure to execute.
-     */
-    public void beforeConfigured(Closure closure) {
-        beforeConfigured.add(closure);
-    }
-
-    /**
-     * <p>Adds an action to be called before the domain object is configured by this task. The domain object is passed
-     * as a parameter to the action.</p>
-     *
-     * <p>The action is executed after the domain object has been loaded from the input file. Using this method allows
-     * you to change the domain object in some way before the task configures it.</p>
-     *
-     * @param action The action to execute.
-     */
-    public void beforeConfigured(Action<? super T> action) {
-        beforeConfigured.add(action);
-    }
-
-    /**
-     * <p>Adds a closure to be called after the domain object has been configured by this task. The domain object is
-     * passed as a parameter to the closure.</p>
-     *
-     * <p>The closure is executed just before the domain object is written to the output file. Using this method allows
-     * you to override the configuration applied by this task.</p>
-     *
-     * @param closure The closure to execute.
-     */
-    public void whenConfigured(Closure closure) {
-        afterConfigured.add(closure);
-    }
-
-    /**
-     * <p>Adds an action to be called after the domain object has been configured by this task. The domain object is
-     * passed as a parameter to the action.</p>
-     *
-     * <p>The action is executed just before the domain object is written to the output file. Using this method allows
-     * you to override the configuration applied by this task.</p>
-     *
-     * @param action The action to execute.
-     */
-    public void whenConfigured(Action<? super T> action) {
-        afterConfigured.add(action);
-    }
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/JavaExec.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/JavaExec.java
index 4398a15..63f8fb3 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/JavaExec.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/JavaExec.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2010 the original author or authors.
+ * Copyright 2011 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ package org.gradle.api.tasks;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.internal.ConventionTask;
 import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.specs.Specs;
 import org.gradle.process.JavaExecSpec;
 import org.gradle.process.JavaForkOptions;
 import org.gradle.process.ProcessForkOptions;
@@ -42,10 +43,12 @@ public class JavaExec extends ConventionTask implements JavaExecSpec {
     public JavaExec() {
         FileResolver fileResolver = getServices().get(FileResolver.class);
         javaExecHandleBuilder = new DefaultJavaExecAction(fileResolver);
+        getOutputs().upToDateWhen(Specs.<Object>satisfyNone());
     }
 
     @TaskAction
     void exec() {
+        setMain(getMain()); // make convention mapping work (at least for 'main')
         javaExecHandleBuilder.execute();
     }
 
@@ -126,6 +129,7 @@ public class JavaExec extends ConventionTask implements JavaExecSpec {
     /**
      * {@inheritDoc}
      */
+    @InputFiles
     public FileCollection getBootstrapClasspath() {
         return javaExecHandleBuilder.getBootstrapClasspath();
     }
@@ -252,6 +256,7 @@ public class JavaExec extends ConventionTask implements JavaExecSpec {
     /**
      * {@inheritDoc}
      */
+    @InputFiles
     public FileCollection getClasspath() {
         return javaExecHandleBuilder.getClasspath();
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskExecutionException.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskExecutionException.java
index 9dfc083..74c7f66 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskExecutionException.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskExecutionException.java
@@ -26,11 +26,6 @@ import org.gradle.api.internal.Contextual;
 public class TaskExecutionException extends GradleException {
     private final Task task;
 
-    // Required for @Contextual
-    public TaskExecutionException(TaskExecutionException source) {
-        this(source.task, source.getCause());
-    }
-    
     public TaskExecutionException(Task task, Throwable cause) {
         super(String.format("Execution failed for %s.", task), cause);
         this.task = task;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskInputs.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskInputs.java
index adfae80..8bf94d7 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskInputs.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskInputs.java
@@ -51,7 +51,7 @@ public interface TaskInputs {
     /**
      * 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)}.
+     * @param path The input file. The given path is evaluated as for {@link org.gradle.api.Project#files(Object...)}.
      * @return this
      */
     TaskInputs file(Object path);
@@ -93,4 +93,46 @@ public interface TaskInputs {
      * @param properties The properties.
      */
     TaskInputs properties(Map<String, ?> properties);
+
+    /**
+     * Returns true if this task has declared that it accepts source files.
+     *
+     * @return true if this task has source files, false if not.
+     */
+    boolean getHasSourceFiles();
+
+    /**
+     * Returns the set of source files for this task. These are the subset of input files which the task does work on.
+     * A task is skipped if it has declared it accepts source files, and this collection is empty.
+     *
+     * @return The set of source files for this task.
+     */
+    FileCollection getSourceFiles();
+
+    /**
+     * Registers some source files for this task. Note that source files are also considered input files, so calling this method implies
+     * a call to {@link #files(Object...)}.
+     *
+     * @param paths The paths. These are evaluated as for {@link org.gradle.api.Project#files(Object...)}.
+     * @return this
+     */
+    TaskInputs source(Object... paths);
+
+    /**
+     * Registers some source files for this task. Note that source files are also considered input files, so calling this method implies
+     * a call to {@link #files(Object...)}.
+     *
+     * @param path The path. This is evaluated as for {@link org.gradle.api.Project#files(Object...)}.
+     * @return this
+     */
+    TaskInputs source(Object path);
+
+    /**
+     * Registers a source directory for this task. All files under this directory are treated as source files for this task. Note that
+     * source files are also considered input files, so calling this method implies a call to {@link #dir(Object)}.
+     *
+     * @param path The path. This is evaluated as for {@link org.gradle.api.Project#file(Object)}.
+     * @return this
+     */
+    TaskInputs sourceDir(Object path);
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskOutputs.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskOutputs.java
index 3ad9fff..9561cd6 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskOutputs.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskOutputs.java
@@ -30,7 +30,7 @@ public interface TaskOutputs {
     /**
      * <p>Adds a predicate to determine whether the outputs of this task are up-to-date. The given closure is executed
      * at task execution time. The closure is passed the task as a parameter. If the closure returns false, the task
-     * outputs are considered out-of-date and the task will be executed executed.</p>
+     * outputs are considered out-of-date and the task will be executed.</p>
      *
      * <p>You can add multiple such predicates. The task outputs are considered out-of-date when any predicate returns
      * false.<p>
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskValidationException.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskValidationException.java
new file mode 100644
index 0000000..442c2ec
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskValidationException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.internal.AbstractMultiCauseException;
+import org.gradle.api.internal.Contextual;
+
+import java.util.List;
+
+/**
+ * A {@code TaskValidationException} is thrown when there is some validation problem with a task.
+ */
+ at Contextual
+public class TaskValidationException extends AbstractMultiCauseException {
+    public TaskValidationException(String message, List<InvalidUserDataException> causes) {
+        super(message, causes);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/XmlGeneratorTask.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/XmlGeneratorTask.java
deleted file mode 100644
index 3f07ed4..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/XmlGeneratorTask.java
+++ /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.tasks;
-
-import groovy.lang.Closure;
-import org.gradle.api.Action;
-import org.gradle.api.artifacts.maven.XmlProvider;
-import org.gradle.api.internal.XmlTransformer;
-import org.gradle.api.internal.tasks.generator.PersistableConfigurationObject;
-import org.gradle.api.internal.tasks.generator.PersistableConfigurationObjectGenerator;
-
-/**
- * A convenience superclass for those tasks which generate XML configuration files from a domain object of type T.
- *
- * @param <T> The domain object type.
- */
-public abstract class XmlGeneratorTask<T extends PersistableConfigurationObject> extends GeneratorTask<T> {
-    private final XmlTransformer xmlTransformer = new XmlTransformer();
-
-    public XmlGeneratorTask() {
-        generator = new PersistableConfigurationObjectGenerator<T>() {
-            public T create() {
-                return XmlGeneratorTask.this.create();
-            }
-
-            public void configure(T object) {
-                XmlGeneratorTask.this.configure(object);
-            }
-        };
-    }
-
-    protected XmlTransformer getXmlTransformer() {
-        return xmlTransformer;
-    }
-
-    protected abstract void configure(T object);
-
-    protected abstract T create();
-
-    /**
-     * Adds a closure to be called when the XML document 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 before
-     * it is written to the output file.
-     *
-     * @param closure The closure to execute when the XML has been created.
-     */
-    public void withXml(Closure closure) {
-        xmlTransformer.addAction(closure);
-    }
-
-    /**
-     * Adds an action to be called when the XML document 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 before
-     * it is written to the output file.
-     *
-     * @param action The action to execute when the IPR XML has been created.
-     */
-    public void withXml(Action<? super XmlProvider> action) {
-        xmlTransformer.addAction(action);
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/util/PatternSet.groovy b/subprojects/core/src/main/groovy/org/gradle/api/tasks/util/PatternSet.groovy
index 6f15567..05fd1e4 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/util/PatternSet.groovy
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/util/PatternSet.groovy
@@ -51,14 +51,18 @@ class PatternSet implements AntBuilderAware, PatternFilterable {
     def boolean caseSensitive = true
 
     static {
-        GLOBAL_EXCLUDES.addAll(DirectoryScanner.DEFAULTEXCLUDES as Collection)
+        resetGlobalExcludes()
     }
 
     static def setGlobalExcludes(Collection<String> excludes) {
         GLOBAL_EXCLUDES.clear()
         GLOBAL_EXCLUDES.addAll(excludes)
     }
-    
+
+    static def resetGlobalExcludes() {
+        setGlobalExcludes(DirectoryScanner.DEFAULTEXCLUDES as Collection)
+    }
+
     def boolean equals(Object o) {
         if (o.is(this)) {
             return true
@@ -87,7 +91,7 @@ class PatternSet implements AntBuilderAware, PatternFilterable {
     public PatternSet intersect() {
         return new IntersectionPatternSet(this)
     }
-    
+
     public Spec<FileTreeElement> getAsSpec() {
         Spec<FileTreeElement> includeSpec = Specs.satisfyAll()
 
@@ -139,7 +143,7 @@ class PatternSet implements AntBuilderAware, PatternFilterable {
     }
 
     public Set<Spec<FileTreeElement>> getExcludeSpecs() {
-       return excludeSpecs
+        return excludeSpecs
     }
 
     public PatternSet setExcludes(Iterable<String> excludes) {
@@ -164,6 +168,7 @@ class PatternSet implements AntBuilderAware, PatternFilterable {
     /*
     This can't be called just include, because it has the same erasure as include(Iterable<String>)
      */
+
     public PatternFilterable includeSpecs(Iterable<Spec<FileTreeElement>> includes) {
         GUtil.addToCollection(this.includeSpecs, includes)
         this
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/DefaultCacheRepository.java b/subprojects/core/src/main/groovy/org/gradle/cache/DefaultCacheRepository.java
index 95f4966..a3f7919 100644
--- a/subprojects/core/src/main/groovy/org/gradle/cache/DefaultCacheRepository.java
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/DefaultCacheRepository.java
@@ -26,7 +26,7 @@ import java.util.HashMap;
 import java.util.Map;
 
 public class DefaultCacheRepository implements CacheRepository {
-    private final GradleVersion version = new GradleVersion();
+    private final GradleVersion version = GradleVersion.current();
     private final File globalCacheDir;
     private final CacheUsage cacheUsage;
     private final CacheFactory factory;
diff --git a/subprojects/core/src/main/groovy/org/gradle/configuration/DefaultScriptPluginFactory.java b/subprojects/core/src/main/groovy/org/gradle/configuration/DefaultScriptPluginFactory.java
index 8ba7773..15a498c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/configuration/DefaultScriptPluginFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/DefaultScriptPluginFactory.java
@@ -31,13 +31,13 @@ public class DefaultScriptPluginFactory implements ScriptPluginFactory {
     private final ImportsReader importsReader;
     private final ScriptHandlerFactory scriptHandlerFactory;
     private final ClassLoader defaultClassLoader;
-    private final Factory<? extends LoggingManagerInternal> loggingManagerFactory;
+    private final Factory<LoggingManagerInternal> loggingManagerFactory;
 
     public DefaultScriptPluginFactory(ScriptCompilerFactory scriptCompilerFactory,
                                                 ImportsReader importsReader,
                                                 ScriptHandlerFactory scriptHandlerFactory,
                                                 ClassLoader defaultClassLoader,
-                                                Factory<? extends LoggingManagerInternal> loggingManagerFactory) {
+                                                Factory<LoggingManagerInternal> loggingManagerFactory) {
         this.scriptCompilerFactory = scriptCompilerFactory;
         this.importsReader = importsReader;
         this.scriptHandlerFactory = scriptHandlerFactory;
diff --git a/subprojects/core/src/main/groovy/org/gradle/configuration/Help.java b/subprojects/core/src/main/groovy/org/gradle/configuration/Help.java
index b8e2a35..362c725 100644
--- a/subprojects/core/src/main/groovy/org/gradle/configuration/Help.java
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/Help.java
@@ -31,7 +31,7 @@ public class Help extends DefaultTask {
         BuildClientMetaData metaData = getServices().get(BuildClientMetaData.class);
 
         output.println();
-        output.formatln("Welcome to Gradle %s.", new GradleVersion().getVersion());
+        output.formatln("Welcome to Gradle %s.", GradleVersion.current().getVersion());
         output.println();
         output.text("To run a build, run ");
         metaData.describeCommand(output.withStyle(UserInput), "<task> ...");
@@ -42,7 +42,7 @@ public class Help extends DefaultTask {
         output.println();
         output.println();
         output.text("To see a list of command-line options, run ");
-        metaData.describeCommand(output.withStyle(UserInput), "-?");
+        metaData.describeCommand(output.withStyle(UserInput), "--help");
         output.println();
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/DefaultScriptCompilationHandler.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/DefaultScriptCompilationHandler.java
index 22878b9..310c3e0 100644
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/DefaultScriptCompilationHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/DefaultScriptCompilationHandler.java
@@ -25,16 +25,19 @@ import org.codehaus.groovy.ast.expr.ConstantExpression;
 import org.codehaus.groovy.ast.stmt.ReturnStatement;
 import org.codehaus.groovy.ast.stmt.Statement;
 import org.codehaus.groovy.control.*;
+import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
 import org.codehaus.groovy.syntax.SyntaxException;
 import org.gradle.api.GradleException;
 import org.gradle.api.ScriptCompilationException;
 import org.gradle.util.Clock;
 import org.gradle.util.GFileUtils;
+import org.gradle.util.UncheckedException;
 import org.gradle.util.WrapUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.File;
+import java.lang.reflect.Field;
 import java.net.URLClassLoader;
 import java.security.CodeSource;
 import java.util.List;
@@ -107,10 +110,7 @@ public class DefaultScriptCompilationHandler implements ScriptCompilationHandler
         try {
             groovyClassLoader.parseClass(codeSource, false);
         } catch (MultipleCompilationErrorsException e) {
-            SyntaxException syntaxError = e.getErrorCollector().getSyntaxError(0);
-            Integer lineNumber = syntaxError == null ? null : syntaxError.getLine();
-            throw new ScriptCompilationException(String.format("Could not compile %s.", source.getDisplayName()), e, source,
-                    lineNumber);
+            wrapCompilationFailure(source, e);
         } catch (CompilationFailedException e) {
             throw new GradleException(String.format("Could not compile %s.", source.getDisplayName()), e);
         }
@@ -124,6 +124,30 @@ public class DefaultScriptCompilationHandler implements ScriptCompilationHandler
         }
     }
 
+    private void wrapCompilationFailure(ScriptSource source, MultipleCompilationErrorsException e) {
+        // Fix the source file name displayed in the error messages
+        for (Object message : e.getErrorCollector().getErrors()) {
+            if (message instanceof SyntaxErrorMessage) {
+                try {
+                    SyntaxErrorMessage syntaxErrorMessage = (SyntaxErrorMessage) message;
+                    Field sourceField = SyntaxErrorMessage.class.getDeclaredField("source");
+                    sourceField.setAccessible(true);
+                    SourceUnit sourceUnit = (SourceUnit) sourceField.get(syntaxErrorMessage);
+                    Field nameField = SourceUnit.class.getDeclaredField("name");
+                    nameField.setAccessible(true);
+                    nameField.set(sourceUnit, source.getDisplayName());
+                } catch (Exception failure) {
+                    throw UncheckedException.asUncheckedException(failure);
+                }
+            }
+        }
+
+        SyntaxException syntaxError = e.getErrorCollector().getSyntaxError(0);
+        Integer lineNumber = syntaxError == null ? null : syntaxError.getLine();
+        throw new ScriptCompilationException(String.format("Could not compile %s.", source.getDisplayName()), e, source,
+                lineNumber);
+    }
+
     private CompilerConfiguration createBaseCompilerConfiguration(Class<? extends Script> scriptBaseClass) {
         CompilerConfiguration configuration = new CompilerConfiguration();
         configuration.setScriptBaseClass(scriptBaseClass.getName());
@@ -141,8 +165,13 @@ public class DefaultScriptCompilationHandler implements ScriptCompilationHandler
                     classLoader);
             return urlClassLoader.loadClass(source.getClassName()).asSubclass(scriptBaseClass);
         } catch (Exception e) {
-            throw new GradleException(String.format("Could not load compiled classes for %s from cache.",
-                    source.getDisplayName()), e);
+            throw new GradleException(String.format("Could not load compiled classes for %s from cache.\n"
+                    + "*****\n"
+                    + "Sometimes this error occurs when the cache was tinkered with.\n"
+                    + "You may try to resolve it by deleting this folder:\n"
+                    + "%s\n"
+                    + "*****\n",
+                    source.getDisplayName(), scriptCacheDir.getAbsolutePath()), e);
         }
     }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/BuildProgressLogger.java b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildProgressLogger.java
index 634da17..c619fb1 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/BuildProgressLogger.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildProgressLogger.java
@@ -36,15 +36,21 @@ class BuildProgressLogger extends BuildAdapter implements TaskExecutionGraphList
     @Override
     public void buildStarted(Gradle gradle) {
         if (gradle.getParent() == null) {
-            progressLogger = progressLoggerFactory.start(BuildProgressLogger.class.getName());
-            progressLogger.progress("Loading");
+            progressLogger = progressLoggerFactory.newOperation(BuildProgressLogger.class);
+            progressLogger.setDescription("Configure projects");
+            progressLogger.setShortDescription("Loading");
+            progressLogger.started();
             this.gradle = gradle;
         }
     }
 
     public void graphPopulated(TaskExecutionGraph graph) {
         if (graph == gradle.getTaskGraph()) {
-            progressLogger.progress("Building");
+            progressLogger.completed();
+            progressLogger = progressLoggerFactory.newOperation(BuildProgressLogger.class);
+            progressLogger.setDescription("Execute tasks");
+            progressLogger.setShortDescription("Building");
+            progressLogger.started();
         }
     }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/ClassLoaderFactory.java b/subprojects/core/src/main/groovy/org/gradle/initialization/ClassLoaderFactory.java
index 1314e7c..bd1cc43 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/ClassLoaderFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/ClassLoaderFactory.java
@@ -1,30 +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.initialization;
-
-import org.gradle.util.MultiParentClassLoader;
-
-public interface ClassLoaderFactory {
-    /**
-     * Returns the root ClassLoader shared by all builds.
-     */
-    ClassLoader getRootClassLoader();
-
-    /**
-     * Creates the script ClassLoader for a build.
-     */
-    MultiParentClassLoader createScriptClassLoader();
-}
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.MultiParentClassLoader;
+
+public interface ClassLoaderFactory {
+    /**
+     * Returns the root class loader shared by all builds.
+     */
+    ClassLoader getRootClassLoader();
+
+    /**
+     * Returns the class loader for the coreImpl project.
+     */
+    ClassLoader getCoreImplClassLoader();
+
+    /**
+     * Creates the script class loader for a build.
+     */
+    MultiParentClassLoader createScriptClassLoader();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultClassLoaderFactory.java b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultClassLoaderFactory.java
index f05e47b..49469d3 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultClassLoaderFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultClassLoaderFactory.java
@@ -1,55 +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.initialization;
-
-import org.apache.tools.ant.Project;
-import org.gradle.api.internal.ClassPathRegistry;
-import org.gradle.util.ClasspathUtil;
-import org.gradle.util.GFileUtils;
-import org.gradle.util.Jvm;
-import org.gradle.util.MultiParentClassLoader;
-
-import java.io.File;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.Collections;
-
-public class DefaultClassLoaderFactory implements ClassLoaderFactory {
-    private final URLClassLoader rootClassLoader;
-
-    public DefaultClassLoaderFactory(ClassPathRegistry classPathRegistry) {
-        // Add in tools.jar to the Ant classloader
-        ClassLoader antClassloader = Project.class.getClassLoader();
-        File toolsJar = Jvm.current().getToolsJar();
-        if (toolsJar != null) {
-            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, runtimeClassloader);
-    }
-
-    public ClassLoader getRootClassLoader() {
-        return rootClassLoader;
-    }
-
-    public MultiParentClassLoader createScriptClassLoader() {
-        return new MultiParentClassLoader(rootClassLoader);
-    }
-}
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.apache.tools.ant.Project;
+import org.gradle.api.internal.ClassPathRegistry;
+import org.gradle.util.ClasspathUtil;
+import org.gradle.util.GFileUtils;
+import org.gradle.util.Jvm;
+import org.gradle.util.MultiParentClassLoader;
+
+import java.io.File;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Collections;
+
+public class DefaultClassLoaderFactory implements ClassLoaderFactory {
+    private final URLClassLoader rootClassLoader;
+    private final URLClassLoader coreImplClassLoader;
+
+    public DefaultClassLoaderFactory(ClassPathRegistry classPathRegistry) {
+        // Add in tools.jar to the Ant classloader
+        ClassLoader antClassloader = Project.class.getClassLoader();
+        File toolsJar = Jvm.current().getToolsJar();
+        if (toolsJar != null) {
+            ClasspathUtil.addUrl((URLClassLoader) antClassloader, GFileUtils.toURLs(Collections.singleton(toolsJar)));
+        }
+
+        // Add in libs for plugins
+        ClassLoader runtimeClassloader = getClass().getClassLoader();
+        URL[] pluginsClassPath = classPathRegistry.getClassPathUrls("GRADLE_PLUGINS");
+        rootClassLoader = new URLClassLoader(pluginsClassPath, runtimeClassloader);
+
+        URL[] coreImplClassPath = classPathRegistry.getClassPathUrls("GRADLE_CORE_IMPL");
+        coreImplClassLoader = new URLClassLoader(coreImplClassPath, rootClassLoader);
+    }
+
+    public ClassLoader getRootClassLoader() {
+        return rootClassLoader;
+    }
+
+    public ClassLoader getCoreImplClassLoader() {
+        return coreImplClassLoader;
+    }
+
+    public MultiParentClassLoader createScriptClassLoader() {
+        return new MultiParentClassLoader(rootClassLoader);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultCommandLineConverter.java b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultCommandLineConverter.java
index 3ba97d1..d4f4280 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultCommandLineConverter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultCommandLineConverter.java
@@ -46,7 +46,9 @@ public class DefaultCommandLineConverter extends AbstractCommandLineConverter<St
     private static final String PROPERTIES = "r";
     private static final String DEPENDENCIES = "n";
     public static final String FULL_STACKTRACE = "S";
+    public static final String FULL_STACKTRACE_LONG = "full-stacktrace";
     public static final String STACKTRACE = "s";
+    public static final String STACKTRACE_LONG = "stacktrace";
     private static final String SYSTEM_PROP = "D";
     private static final String PROJECT_PROP = "P";
     public static final String GRADLE_USER_HOME = "g";
@@ -75,8 +77,8 @@ public class DefaultCommandLineConverter extends AbstractCommandLineConverter<St
         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(STACKTRACE, STACKTRACE_LONG).hasDescription("Print out the stacktrace also for user exceptions (e.g. compile error).");
+        parser.option(FULL_STACKTRACE, FULL_STACKTRACE_LONG).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].");
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultExceptionAnalyser.java b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultExceptionAnalyser.java
index cee2873..4de6a82 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultExceptionAnalyser.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultExceptionAnalyser.java
@@ -20,19 +20,18 @@ import org.gradle.api.LocationAwareException;
 import org.gradle.api.ScriptCompilationException;
 import org.gradle.api.internal.Contextual;
 import org.gradle.api.internal.ExceptionAnalyser;
-import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.tasks.TaskExecutionException;
 import org.gradle.groovy.scripts.Script;
 import org.gradle.groovy.scripts.ScriptExecutionListener;
 import org.gradle.groovy.scripts.ScriptSource;
 import org.gradle.listener.ListenerManager;
+import org.gradle.listener.ListenerNotificationException;
 
 import java.util.HashMap;
 import java.util.Map;
 
 public class DefaultExceptionAnalyser implements ExceptionAnalyser, ScriptExecutionListener {
     private final Map<String, ScriptSource> scripts = new HashMap<String, ScriptSource>();
-    private final ExceptionDecoratingClassGenerator generator = new ExceptionDecoratingClassGenerator();
 
     public DefaultExceptionAnalyser(ListenerManager listenerManager) {
         listenerManager.addListener(this);
@@ -57,13 +56,17 @@ public class DefaultExceptionAnalyser implements ExceptionAnalyser, ScriptExecut
 
         ScriptSource source = null;
         Integer lineNumber = null;
+        Throwable target = actualException;
 
-        // todo - remove this special case
+        // todo - remove these special cases
         if (actualException instanceof ScriptCompilationException) {
             ScriptCompilationException scriptCompilationException = (ScriptCompilationException) actualException;
             source = scriptCompilationException.getScriptSource();
             lineNumber = scriptCompilationException.getLineNumber();
         }
+        if (actualException instanceof ListenerNotificationException && actualException.getCause() != null) {
+            target = actualException.getCause();
+        }
 
         if (source == null) {
             for (
@@ -79,27 +82,28 @@ public class DefaultExceptionAnalyser implements ExceptionAnalyser, ScriptExecut
             }
         }
 
-        if (source == null) {
-            if (actualException instanceof TaskExecutionException) {
-                TaskExecutionException taskExecutionException = (TaskExecutionException) actualException;
-                source = ((ProjectInternal) taskExecutionException.getTask().getProject()).getBuildScriptSource();
-            }
-        }
-
-        return generator.newInstance(actualException.getClass(), actualException, source, lineNumber);
+        return new LocationAwareException(actualException, target, source, lineNumber);
     }
 
     private Throwable findDeepest(Throwable exception) {
+        Throwable locationAware = null;
         Throwable result = null;
         Throwable contextMatch = null;
         for (Throwable current = exception; current != null; current = current.getCause()) {
-            if (current instanceof GradleScriptException || current instanceof TaskExecutionException) {
+            if (current instanceof LocationAwareException) {
+                locationAware = current;
+            } else if (current instanceof GradleScriptException || current instanceof TaskExecutionException) {
                 result = current;
-            }
-            if (current.getClass().getAnnotation(Contextual.class) != null) {
+            } else if (current.getClass().getAnnotation(Contextual.class) != null) {
                 contextMatch = current;
             }
         }
-        return result != null ? result : contextMatch;
+        if (locationAware != null) {
+            return locationAware;
+        } else if (result != null) {
+            return result;
+        } else {
+            return contextMatch;
+        }
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultGradleLauncher.java b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultGradleLauncher.java
index 9b06b13..29b2850 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultGradleLauncher.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultGradleLauncher.java
@@ -18,6 +18,7 @@ package org.gradle.initialization;
 import org.gradle.BuildListener;
 import org.gradle.BuildResult;
 import org.gradle.GradleLauncher;
+import org.gradle.StartParameter;
 import org.gradle.api.internal.ExceptionAnalyser;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.SettingsInternal;
@@ -64,6 +65,10 @@ public class DefaultGradleLauncher extends GradleLauncher {
         this.loggingManager = loggingManager;
     }
 
+    public GradleInternal getGradle() {
+        return gradle;
+    }
+
     /**
      * <p>Executes the build for this GradleLauncher instance and returns the result. Note that when the build fails,
      * the exception is available using {@link org.gradle.BuildResult#getFailure()}.</p>
@@ -200,4 +205,9 @@ public class DefaultGradleLauncher extends GradleLauncher {
     public void addStandardErrorListener(StandardOutputListener listener) {
         loggingManager.addStandardErrorListener(listener);
     }
+
+    @Override
+    public StartParameter getStartParameter() {
+        return gradle.getStartParameter();
+    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultGradleLauncherFactory.java b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultGradleLauncherFactory.java
index c11b6a2..fe8b1b3 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultGradleLauncherFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultGradleLauncherFactory.java
@@ -54,11 +54,6 @@ public class DefaultGradleLauncherFactory implements GradleLauncherFactory {
 
     private DefaultGradleLauncherFactory(GlobalServicesRegistry globalServices) {
         sharedServices = globalServices;
-
-        // Start logging system
-//        sharedServices.newInstance(LoggingManagerInternal.class).setLevel(LogLevel.LIFECYCLE).start();
-
-        commandLineConverter = sharedServices.get(CommandLineConverter.class);
         tracker = new NestedBuildTracker();
 
         // Register default loggers 
@@ -77,6 +72,9 @@ public class DefaultGradleLauncherFactory implements GradleLauncherFactory {
     }
 
     public StartParameter createStartParameter(String... commandLineArgs) {
+        if (commandLineConverter == null) {
+            commandLineConverter = sharedServices.get(CommandLineConverter.class);
+        }
         return commandLineConverter.convert(Arrays.asList(commandLineArgs));
     }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/ExceptionDecoratingClassGenerator.java b/subprojects/core/src/main/groovy/org/gradle/initialization/ExceptionDecoratingClassGenerator.java
deleted file mode 100644
index 3ff6e0a..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/ExceptionDecoratingClassGenerator.java
+++ /dev/null
@@ -1,230 +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.apache.commons.lang.StringUtils;
-import org.gradle.api.LocationAwareException;
-import org.gradle.api.internal.ClassGenerator;
-import org.gradle.api.internal.Contextual;
-import org.gradle.groovy.scripts.ScriptSource;
-import org.gradle.util.ReflectionUtil;
-import org.objectweb.asm.ClassWriter;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
-
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * A {@link ClassGenerator} which mixes {@link org.gradle.api.LocationAwareException} into the supplied exception
- * types. Uses {@link ExceptionHelper} to do the work.
- */
-public class ExceptionDecoratingClassGenerator implements ClassGenerator {
-    private static final Map<Class<?>, Class<?>> GENERATED_CLASSES = new HashMap<Class<?>, Class<?>>();
-
-    public <T> T newInstance(Class<T> type, Object... parameters) {
-        Throwable throwable = ReflectionUtil.newInstance(generate(type), parameters);
-        throwable.setStackTrace(((Throwable) parameters[0]).getStackTrace());
-        return type.cast(throwable);
-    }
-
-    public <T> Class<? extends T> generate(Class<T> type) {
-        Class generated = GENERATED_CLASSES.get(type);
-        if (generated == null) {
-            generated = doGenerate(type);
-            GENERATED_CLASSES.put(type, generated);
-        }
-        return generated;
-    }
-
-    private <T> Class<? extends T> doGenerate(Class<T> type) {
-        ClassWriter visitor = new ClassWriter(ClassWriter.COMPUTE_MAXS);
-        String typeName = StringUtils.substringBeforeLast(type.getName(), ".") + ".LocationAware" + type.getSimpleName();
-        Type generatedType = Type.getType("L" + typeName.replaceAll("\\.", "/") + ";");
-        Type superclassType = Type.getType(type);
-
-        visitor.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC, generatedType.getInternalName(), null,
-                superclassType.getInternalName(), new String[]{Type.getType(LocationAwareException.class).getInternalName()});
-
-        Type helperType = Type.getType(ExceptionHelper.class);
-        Type throwableType = Type.getType(Throwable.class);
-        Type scriptSourceType = Type.getType(ScriptSource.class);
-        Type integerType = Type.getType(Integer.class);
-
-        // GENERATE private ExceptionHelper helper;
-        visitor.visitField(Opcodes.ACC_PRIVATE, "helper", helperType.getDescriptor(), null, null);
-
-        // GENERATE <init>(<type> target, ScriptSource source, Integer lineNumber)
-
-        String methodDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE,
-                new Type[]{superclassType, scriptSourceType, integerType});
-        MethodVisitor methodVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, "<init>", methodDescriptor, null,
-                new String[0]);
-        methodVisitor.visitCode();
-
-        boolean noArgsConstructor;
-        try {
-            type.getConstructor(type);
-            noArgsConstructor = false;
-        } catch (NoSuchMethodException e) {
-            try {
-                type.getConstructor();
-                noArgsConstructor = true;
-            } catch (NoSuchMethodException e1) {
-                throw new IllegalArgumentException(String.format(
-                        "Cannot create subtype for exception '%s'. It needs a zero-args or copy constructor.",
-                        type.getName()));
-            }
-        }
-
-        if (noArgsConstructor) {
-            // GENERATE super()
-            methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
-            methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, superclassType.getInternalName(), "<init>",
-                    Type.getMethodDescriptor(Type.VOID_TYPE, new Type[0]));
-            // END super()
-        } else {
-            // GENERATE super(target)
-            methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
-            methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
-            methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, superclassType.getInternalName(), "<init>",
-                    Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{superclassType}));
-            // END super(target)
-        }
-
-        // GENERATE helper = new ExceptionHelper(this, target, source, lineNumber)
-        methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
-
-        methodVisitor.visitTypeInsn(Opcodes.NEW, helperType.getInternalName());
-        methodVisitor.visitInsn(Opcodes.DUP);
-        methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
-        methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
-        methodVisitor.visitVarInsn(Opcodes.ALOAD, 2);
-        methodVisitor.visitVarInsn(Opcodes.ALOAD, 3);
-        methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, helperType.getInternalName(), "<init>",
-                Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{throwableType, throwableType, scriptSourceType, integerType}));
-
-        methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, generatedType.getInternalName(), "helper",
-                helperType.getDescriptor());
-
-        // END helper = new ExceptionHelper(target)
-
-        methodVisitor.visitInsn(Opcodes.RETURN);
-        methodVisitor.visitMaxs(0, 0);
-        methodVisitor.visitEnd();
-
-        // END <init>(<type> target, ScriptSource source, Integer lineNumber)
-
-        for (Method method : ExceptionHelper.class.getDeclaredMethods()) {
-            // GENERATE public <type> <method>() { return helper.<method>(); }
-            methodDescriptor = Type.getMethodDescriptor(Type.getType(method.getReturnType()), new Type[0]);
-            methodVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, method.getName(), methodDescriptor, null,
-                    new String[0]);
-            methodVisitor.visitCode();
-
-            methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
-            methodVisitor.visitFieldInsn(Opcodes.GETFIELD, generatedType.getInternalName(), "helper",
-                    helperType.getDescriptor());
-            methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, helperType.getInternalName(), method.getName(),
-                    methodDescriptor);
-
-            methodVisitor.visitInsn(Opcodes.ARETURN);
-            methodVisitor.visitMaxs(0, 0);
-            methodVisitor.visitEnd();
-            // END public <type> <method>() { return helper.<method>(); }
-        }
-
-        visitor.visitEnd();
-
-        byte[] bytecode = visitor.toByteArray();
-        return (Class<T>) ReflectionUtil.invoke(type.getClassLoader(), "defineClass", new Object[]{
-                typeName, bytecode, 0, bytecode.length
-        });
-    }
-
-    public static class ExceptionHelper {
-        private final Throwable target;
-        private final ScriptSource source;
-        private final Integer lineNumber;
-
-        public ExceptionHelper(Throwable owner, Throwable target, ScriptSource source, Integer lineNumber) {
-            if (owner.getCause() == null) {
-                owner.initCause(target.getCause());
-            }
-            this.target = target;
-            this.source = source;
-            this.lineNumber = lineNumber;
-        }
-
-        public String getOriginalMessage() {
-            return target.getMessage();
-        }
-
-        public Throwable getOriginalException() {
-            return target;
-        }
-
-        public String getLocation() {
-            if (source == null) {
-                return null;
-            }
-            String sourceMsg = StringUtils.capitalize(source.getDisplayName());
-            if (lineNumber == null) {
-                return sourceMsg;
-            }
-            return String.format("%s line: %d", sourceMsg, lineNumber);
-        }
-
-        public String getMessage() {
-            String location = getLocation();
-            String message = target.getMessage();
-            if (location == null && message == null) {
-                return null;
-            }
-            if (location == null) {
-                return message;
-            }
-            if (message == null) {
-                return location;
-            }
-            return String.format("%s%n%s", location, message);
-        }
-
-        public ScriptSource getScriptSource() {
-            return source;
-        }
-
-        public Integer getLineNumber() {
-            return lineNumber;
-        }
-
-        public List<Throwable> getReportableCauses() {
-            ArrayList<Throwable> causes = new ArrayList<Throwable>();
-            for (Throwable t = target.getCause(); t != null; t = t.getCause()) {
-                causes.add(t);
-                if (t.getClass().getAnnotation(Contextual.class) == null) {
-                    break;
-                }
-            }
-            return causes;
-        }
-
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/GradleLauncherAction.java b/subprojects/core/src/main/groovy/org/gradle/initialization/GradleLauncherAction.java
new file mode 100644
index 0000000..7184515
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/GradleLauncherAction.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.BuildResult;
+import org.gradle.GradleLauncher;
+
+public interface GradleLauncherAction<T> {
+    T getResult();
+
+    BuildResult run(GradleLauncher launcher);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/invocation/DefaultGradle.java b/subprojects/core/src/main/groovy/org/gradle/invocation/DefaultGradle.java
index 669af4f..412ff7d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/invocation/DefaultGradle.java
+++ b/subprojects/core/src/main/groovy/org/gradle/invocation/DefaultGradle.java
@@ -66,7 +66,7 @@ public class DefaultGradle implements GradleInternal {
     }
 
     public String getGradleVersion() {
-        return new GradleVersion().getVersion();
+        return GradleVersion.current().getVersion();
     }
 
     public File getGradleHomeDir() {
diff --git a/subprojects/core/src/main/groovy/org/gradle/listener/ListenerNotificationException.java b/subprojects/core/src/main/groovy/org/gradle/listener/ListenerNotificationException.java
index dfbf075..e9921a3 100644
--- a/subprojects/core/src/main/groovy/org/gradle/listener/ListenerNotificationException.java
+++ b/subprojects/core/src/main/groovy/org/gradle/listener/ListenerNotificationException.java
@@ -23,10 +23,6 @@ import org.gradle.api.internal.Contextual;
  */
 @Contextual
 public class ListenerNotificationException extends GradleException {
-    // Required for @Contextual
-    public ListenerNotificationException() {
-    }
-
     public ListenerNotificationException(String message, Throwable cause) {
         super(message, cause);
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/LoggingServiceRegistry.java b/subprojects/core/src/main/groovy/org/gradle/logging/LoggingServiceRegistry.java
index 5ee4b57..04f5578 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/LoggingServiceRegistry.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/LoggingServiceRegistry.java
@@ -16,23 +16,55 @@
 
 package org.gradle.logging;
 
+import org.gradle.StartParameter;
 import org.gradle.api.internal.Factory;
 import org.gradle.api.internal.project.DefaultServiceRegistry;
+import org.gradle.api.specs.Spec;
+import org.gradle.api.specs.Specs;
 import org.gradle.initialization.CommandLineConverter;
 import org.gradle.logging.internal.*;
 import org.gradle.util.TimeProvider;
 import org.gradle.util.TrueTimeProvider;
 
+import java.io.FileDescriptor;
+
 /**
  * A {@link org.gradle.api.internal.project.ServiceRegistry} implementation which provides the logging services.
  */
 public class LoggingServiceRegistry extends DefaultServiceRegistry {
     private TextStreamOutputEventListener stdoutListener;
+    private final boolean detectConsole;
+
+    LoggingServiceRegistry() {
+        this(true);
+    }
 
-    public LoggingServiceRegistry() {
+    LoggingServiceRegistry(boolean detectConsole) {
+        this.detectConsole = detectConsole;
         stdoutListener = new TextStreamOutputEventListener(get(OutputEventListener.class));
     }
 
+    /**
+     * Creates a set of logging services which are suitable to use in a command-line process.
+     */
+    public static LoggingServiceRegistry newCommandLineProcessLogging() {
+        return new LoggingServiceRegistry(true);
+    }
+
+    /**
+     * Creates a set of logging services which are suitable to use in a child process. Does not attempt to use any terminal trickery.
+     */
+    public static LoggingServiceRegistry newChildProcessLogging() {
+        return new LoggingServiceRegistry(false);
+    }
+
+    /**
+     * Creates a set of logging services which are suitable to use embedded in another application. Does not attempt to use any terminal trickery.
+     */
+    public static LoggingServiceRegistry newEmbeddableLogging() {
+        return new LoggingServiceRegistry(false);
+    }
+
     protected CommandLineConverter<LoggingConfiguration> createCommandLineConverter() {
         return new LoggingCommandLineConverter();
     }
@@ -61,10 +93,24 @@ public class LoggingServiceRegistry extends DefaultServiceRegistry {
         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));
+        return new DefaultLoggingManagerFactory(compositeConfigurer, renderer, getStdOutLoggingSystem(), getStdErrLoggingSystem());
     }
-    
+
+    private LoggingSystem getStdErrLoggingSystem() {
+        return get(StdErrLoggingSystem.class);
+    }
+
+    private LoggingSystem getStdOutLoggingSystem() {
+        return get(StdOutLoggingSystem.class);
+    }
+
     protected OutputEventRenderer createOutputEventRenderer() {
-        return new OutputEventRenderer().addStandardOutputAndError();
+        Spec<FileDescriptor> terminalDetector;
+        if (detectConsole) {
+            terminalDetector = new TerminalDetector(StartParameter.DEFAULT_GRADLE_USER_HOME);
+        } else {
+            terminalDetector = Specs.satisfyNone();
+        }
+        return new OutputEventRenderer(terminalDetector).addStandardOutputAndError();
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/ProgressLogger.java b/subprojects/core/src/main/groovy/org/gradle/logging/ProgressLogger.java
index b786deb..c2665b9 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/ProgressLogger.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/ProgressLogger.java
@@ -18,19 +18,90 @@ package org.gradle.logging;
 
 /**
  * Used to log the progress of a potentially long running operation.
+ *
+ * <p>When running in the command-line UI, the properties of an operation are treated as follows:
+ *
+ * <ul>
+ *
+ * <li>When an operation starts, and the operation has a logging header defined, a LIFECYCLE log message is generated containing the logging header.
+ * If running under a terminal, and the logging header == the short description or the status, this log message is deferred until either some other log
+ * message is generated or the operation completes.</li>
+ *
+ * <li>If running under a terminal, and the operation has a status defined, that status is shown in the 'status bar' at the bottom of the screen.</li>
+ *
+ * <li>If running under a terminal, and the operation has a short description and no status defined, the short description is shown in the 'status bar' at the bottom of the screen.</li>
+ *
+ * </ul>
+ *
+ * </p>
  */
 public interface ProgressLogger {
     /**
-     * Returns the description of the operation. The description is generally logged at the start of the operation.
+     * Returns the description of the operation.
      *
-     * @return the description, possibly empty.
+     * @return the description, must not be empty.
      */
     String getDescription();
 
     /**
+     * <p>Sets the description of the operation. This should be a full, stand-alone description of the operation.
+     *
+     * <p>This must be called before {@link #started()}.
+     *
+     * @param description The description.
+     */
+    void setDescription(String description);
+
+    /**
+     * Returns the short description of the operation. This is used in place of the full description when display space is limited.
+     *
+     * @return The short description, must not be empty.
+     */
+    String getShortDescription();
+
+    /**
+     * <p>Sets the short description of the operation. This is used in place of the full description when display space is limited.
+     *
+     * <p>This must be called before {@link #started()}
+     *
+     * @param description The short description.
+     */
+    void setShortDescription(String description);
+
+    /**
+     * <p>Returns the logging header for the operation. This is logged before any other log messages for this operation are logged. It is usually
+     * also logged at the end of the operation, along with the final status message. Defaults to null.
+     *
+     * <p>If not specified, no logging header is logged.
+     *
+     * @return The logging header, possibly empty.
+     */
+    String getLoggingHeader();
+
+    /**
+     * <p>Sets the logging header for the operation. This is logged before any other log messages for this operation are logged. It is usually
+     * also logged at the end of the operation, along with the final status message. Defaults to null.
+     *
+     * @param header The header. May be empty or null.
+     */
+    void setLoggingHeader(String header);
+
+    /**
+     * Logs the start of the operation, with no initial status.
+     */
+    void started();
+
+    /**
+     * Logs the start of the operation, with the given status.
+     *
+     * @param status The initial status message. Can be null or empty.
+     */
+    void started(String status);
+
+    /**
      * Logs some progress, indicated by a new status.
      *
-     * @param status The new status message
+     * @param status The new status message. Can be null or empty.
      */
     void progress(String status);
 
@@ -42,14 +113,7 @@ public interface ProgressLogger {
     /**
      * Logs the completion of the operation, with a final status. This is generally logged along with the description.
      *
-     * @param status The final status message
+     * @param status The final status message. Can be null or empty.
      */
     void completed(String status);
-
-    /**
-     * Returns the current status of the operation.
-     *
-     * @return The status, possibly empty.
-     */
-    String getStatus();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/ProgressLoggerFactory.java b/subprojects/core/src/main/groovy/org/gradle/logging/ProgressLoggerFactory.java
index 199b503..516563c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/ProgressLoggerFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/ProgressLoggerFactory.java
@@ -18,19 +18,18 @@ package org.gradle.logging;
 
 public interface ProgressLoggerFactory {
     /**
-     * Starts an operation, with no description.
+     * Creates a new long-running operation which has not been started.
      *
      * @param loggerCategory The logger category.
      * @return The progress logger for the operation.
      */
-    ProgressLogger start(String loggerCategory);
+    ProgressLogger newOperation(String loggerCategory);
 
     /**
-     * Starts an operation. The description is generally displayed in a log message.
+     * Creates a new long-running operation which has not been started.
      *
      * @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);
+    ProgressLogger newOperation(Class loggerCategory);
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/AbstractStyledTextOutput.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/AbstractStyledTextOutput.java
index 36d20d1..2b52c3f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/AbstractStyledTextOutput.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/AbstractStyledTextOutput.java
@@ -17,12 +17,12 @@ package org.gradle.logging.internal;
 
 import org.gradle.api.logging.StandardOutputListener;
 import org.gradle.logging.StyledTextOutput;
+import org.gradle.util.SystemProperties;
 
 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) {
@@ -62,7 +62,7 @@ public abstract class AbstractStyledTextOutput implements StyledTextOutput, Stan
     }
 
     public StyledTextOutput println() {
-        text(EOL);
+        text(SystemProperties.getLineSeparator());
         return this;
     }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/AnsiConsole.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/AnsiConsole.java
index 956bf20..2837a53 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/AnsiConsole.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/AnsiConsole.java
@@ -20,13 +20,15 @@ import org.apache.commons.lang.StringUtils;
 import org.fusesource.jansi.Ansi;
 import org.gradle.api.Action;
 import org.gradle.api.UncheckedIOException;
+import org.gradle.util.SystemProperties;
 
 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 static String EOL = SystemProperties.getLineSeparator();
+
     private final Appendable target;
     private final Flushable flushable;
     private LabelImpl statusBar;
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/ConsoleBackedProgressRenderer.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/ConsoleBackedProgressRenderer.java
index 8ef3cf7..b246c43 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/ConsoleBackedProgressRenderer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/ConsoleBackedProgressRenderer.java
@@ -15,12 +15,14 @@
  */
 package org.gradle.logging.internal;
 
+import org.gradle.util.GUtil;
+
 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 final LinkedList<Operation> operations = new LinkedList<Operation>();
     private Label statusBar;
 
     public ConsoleBackedProgressRenderer(OutputEventListener listener, Console console) {
@@ -30,14 +32,15 @@ public class ConsoleBackedProgressRenderer implements OutputEventListener {
 
     public void onOutput(OutputEvent event) {
         if (event instanceof ProgressStartEvent) {
-            operations.addLast("");
+            ProgressStartEvent startEvent = (ProgressStartEvent) event;
+            operations.addLast(new Operation(startEvent.getShortDescription(), startEvent.getStatus()));
+            updateText();
         } else if (event instanceof ProgressCompleteEvent) {
             operations.removeLast();
             updateText();
         } else if (event instanceof ProgressEvent) {
             ProgressEvent progressEvent = (ProgressEvent) event;
-            operations.removeLast();
-            operations.addLast(progressEvent.getStatus());
+            operations.getLast().status = progressEvent.getStatus();
             updateText();
         }
         listener.onOutput(event);
@@ -45,19 +48,41 @@ public class ConsoleBackedProgressRenderer implements OutputEventListener {
 
     private void updateText() {
         StringBuilder builder = new StringBuilder();
-        for (String operation : operations) {
-            if (operation.length() == 0) {
+        for (Operation operation : operations) {
+            String message = operation.getMessage();
+            if (message == null) {
                 continue;
             }
             if (builder.length() > 0) {
                 builder.append(' ');
             }
             builder.append("> ");
-            builder.append(operation);
+            builder.append(message);
         }
         if (statusBar == null) {
             statusBar = console.getStatusBar();
         }
         statusBar.setText(builder.toString());
     }
+
+    private static class Operation {
+        private final String shortDescription;
+        private String status;
+
+        private Operation(String shortDescription, String status) {
+            this.shortDescription = shortDescription;
+            this.status = status;
+        }
+
+        String getMessage() {
+            if (GUtil.isTrue(status)) {
+                return status;
+            }
+            if (GUtil.isTrue(shortDescription)) {
+                return shortDescription;
+            }
+            return null;
+        }
+    }
+
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/DefaultLoggingManager.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/DefaultLoggingManager.java
index 1d13ec5..fb138bc 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/DefaultLoggingManager.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/DefaultLoggingManager.java
@@ -36,6 +36,7 @@ public class DefaultLoggingManager implements LoggingManagerInternal {
     private final LoggingOutputInternal loggingOutput;
     private final Set<StandardOutputListener> stdoutListeners = new LinkedHashSet<StandardOutputListener>();
     private final Set<StandardOutputListener> stderrListeners = new LinkedHashSet<StandardOutputListener>();
+    private final Set<OutputEventListener> outputEventListeners = new LinkedHashSet<OutputEventListener>();
 
     public DefaultLoggingManager(LoggingSystem loggingSystem, LoggingSystem stdOutLoggingSystem,
                                  LoggingSystem stdErrLoggingSystem, LoggingOutputInternal loggingOutput) {
@@ -53,6 +54,9 @@ public class DefaultLoggingManager implements LoggingManagerInternal {
         for (StandardOutputListener stderrListener : stderrListeners) {
             loggingOutput.addStandardErrorListener(stderrListener);
         }
+        for (OutputEventListener outputEventListener : outputEventListeners) {
+            loggingOutput.addOutputEventListener(outputEventListener);
+        }
         loggingSystem.start();
         stdOutLoggingSystem.start();
         stdErrLoggingSystem.start();
@@ -69,6 +73,9 @@ public class DefaultLoggingManager implements LoggingManagerInternal {
             for (StandardOutputListener stderrListener : stderrListeners) {
                 loggingOutput.removeStandardErrorListener(stderrListener);
             }
+            for (OutputEventListener listener : outputEventListeners) {
+                loggingOutput.removeOutputEventListener(listener);
+            }
         } finally {
             started = false;
         }
@@ -137,11 +144,15 @@ public class DefaultLoggingManager implements LoggingManagerInternal {
     }
 
     public void addOutputEventListener(OutputEventListener listener) {
-        loggingOutput.addOutputEventListener(listener);
+        if (outputEventListeners.add(listener) && started) {
+            loggingOutput.addOutputEventListener(listener);
+        }
     }
 
     public void removeOutputEventListener(OutputEventListener listener) {
-        loggingOutput.removeOutputEventListener(listener);
+        if (outputEventListeners.remove(listener) && started) {
+            loggingOutput.removeOutputEventListener(listener);
+        }
     }
 
     public void colorStdOutAndStdErr(boolean colorOutput) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/DefaultProgressLoggerFactory.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/DefaultProgressLoggerFactory.java
index 43e895f..e240cb5 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/DefaultProgressLoggerFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/DefaultProgressLoggerFactory.java
@@ -18,6 +18,7 @@ package org.gradle.logging.internal;
 
 import org.gradle.logging.ProgressLogger;
 import org.gradle.logging.ProgressLoggerFactory;
+import org.gradle.util.GUtil;
 import org.gradle.util.TimeProvider;
 
 public class DefaultProgressLoggerFactory implements ProgressLoggerFactory {
@@ -29,27 +30,26 @@ public class DefaultProgressLoggerFactory implements ProgressLoggerFactory {
         this.timeProvider = timeProvider;
     }
 
-    public ProgressLogger start(String loggerCategory) {
-        return start(loggerCategory, "");
+    public ProgressLogger newOperation(Class loggerCategory) {
+        return newOperation(loggerCategory.getName());
     }
 
-    public ProgressLogger start(String loggerCategory, String description) {
-        ProgressLoggerImpl logger = new ProgressLoggerImpl(loggerCategory, description, progressListener, timeProvider);
-        logger.started();
-        return logger;
+    public ProgressLogger newOperation(String loggerCategory) {
+        return new ProgressLoggerImpl(loggerCategory, progressListener, timeProvider);
     }
 
     private static class ProgressLoggerImpl implements ProgressLogger {
+        private enum State { idle, started, completed }
         private final String category;
-        private final String description;
         private final ProgressListener listener;
         private final TimeProvider timeProvider;
-        private String status = "";
-        private boolean completed;
+        private String description;
+        private String shortDescription;
+        private String loggingHeader;
+        private State state = State.idle;
 
-        public ProgressLoggerImpl(String category, String description, ProgressListener listener, TimeProvider timeProvider) {
+        public ProgressLoggerImpl(String category, ProgressListener listener, TimeProvider timeProvider) {
             this.category = category;
-            this.description = description;
             this.listener = listener;
             this.timeProvider = timeProvider;
         }
@@ -58,33 +58,81 @@ public class DefaultProgressLoggerFactory implements ProgressLoggerFactory {
             return description;
         }
 
-        public String getStatus() {
-            return status;
+        public void setDescription(String description) {
+            assertCanConfigure();
+            this.description = description;
+        }
+
+        public String getShortDescription() {
+            return shortDescription;
+        }
+
+        public void setShortDescription(String shortDescription) {
+            assertCanConfigure();
+            this.shortDescription = shortDescription;
+        }
+
+        public String getLoggingHeader() {
+            return loggingHeader;
+        }
+
+        public void setLoggingHeader(String loggingHeader) {
+            assertCanConfigure();
+            this.loggingHeader = loggingHeader;
         }
 
         public void started() {
-            listener.started(new ProgressStartEvent(timeProvider.getCurrentTime(), category, description));
+            started(null);
+        }
+
+        public void started(String status) {
+            if (!GUtil.isTrue(description)) {
+                throw new IllegalStateException("A description must be specified before this operation is started.");
+            }
+            if (state == State.started) {
+                throw new IllegalStateException("This operation has already been started.");
+            }
+            assertNotCompleted();
+            state = State.started;
+            listener.started(new ProgressStartEvent(timeProvider.getCurrentTime(), category, description, shortDescription, loggingHeader, toStatus(status)));
         }
 
         public void progress(String status) {
+            assertStarted();
             assertNotCompleted();
-            this.status = status;
-            listener.progress(new ProgressEvent(timeProvider.getCurrentTime(), category, status));
+            listener.progress(new ProgressEvent(timeProvider.getCurrentTime(), category, toStatus(status)));
         }
 
         public void completed() {
-            completed("");
+            completed(null);
         }
 
         public void completed(String status) {
-            this.status = status;
-            completed = true;
-            listener.completed(new ProgressCompleteEvent(timeProvider.getCurrentTime(), category, status));
+            assertStarted();
+            assertNotCompleted();
+            state = State.completed;
+            listener.completed(new ProgressCompleteEvent(timeProvider.getCurrentTime(), category, toStatus(status)));
+        }
+
+        private String toStatus(String status) {
+            return status == null ? "" : status;
         }
 
         private void assertNotCompleted() {
-            if (completed) {
-                throw new IllegalStateException("This ProgressLogger has been completed.");
+            if (state == ProgressLoggerImpl.State.completed) {
+                throw new IllegalStateException("This operation has completed.");
+            }
+        }
+
+        private void assertStarted() {
+            if (state == ProgressLoggerImpl.State.idle) {
+                throw new IllegalStateException("This operation has not been started.");
+            }
+        }
+
+        private void assertCanConfigure() {
+            if (state != State.idle) {
+                throw new IllegalStateException("Cannot configure this operation once it has started.");
             }
         }
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/LoggingCommandLineConverter.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/LoggingCommandLineConverter.java
index cc29fba..6d77e23 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/LoggingCommandLineConverter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/LoggingCommandLineConverter.java
@@ -29,8 +29,11 @@ import java.util.Collections;
 
 public class LoggingCommandLineConverter extends AbstractCommandLineConverter<LoggingConfiguration> {
     public static final String DEBUG = "d";
+    public static final String DEBUG_LONG = "debug";
     public static final String INFO = "i";
+    public static final String INFO_LONG = "info";
     public static final String QUIET = "q";
+    public static final String QUIET_LONG = "quiet";
     public static final String NO_COLOR = "no-color";
     private final BiMap<String, LogLevel> logLevelMap = HashBiMap.create();
 
@@ -69,9 +72,9 @@ public class LoggingCommandLineConverter extends AbstractCommandLineConverter<Lo
     }
 
     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(DEBUG, DEBUG_LONG).hasDescription("Log in debug mode (includes normal stacktrace).");
+        parser.option(QUIET, QUIET_LONG).hasDescription("Log errors only.");
+        parser.option(INFO, INFO_LONG).hasDescription("Set log level to info.");
         parser.option(NO_COLOR).hasDescription("Do not use color in the console output.");
     }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/OutputEventRenderer.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/OutputEventRenderer.java
index 83d9d07..9ff5a3e 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/OutputEventRenderer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/OutputEventRenderer.java
@@ -15,13 +15,12 @@
  */
 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.api.specs.Spec;
 import org.gradle.listener.ListenerBroadcast;
 
 import java.io.FileDescriptor;
-import java.io.IOException;
 import java.io.PrintStream;
 
 /**
@@ -32,15 +31,17 @@ public class OutputEventRenderer implements OutputEventListener, LoggingConfigur
     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 Spec<FileDescriptor> terminalDetector;
     private final Object lock = new Object();
     private final DefaultColorMap colourMap = new DefaultColorMap();
     private LogLevel logLevel = LogLevel.LIFECYCLE;
 
-    public OutputEventRenderer() {
+    public OutputEventRenderer(Spec<FileDescriptor> terminalDetector) {
         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);
+        this.terminalDetector = terminalDetector;
     }
 
     public void colorStdOutAndStdErr(boolean colorOutput) {
@@ -50,7 +51,6 @@ public class OutputEventRenderer implements OutputEventListener, LoggingConfigur
     }
 
     public OutputEventRenderer addStandardOutputAndError() {
-        TerminalDetector terminalDetector = new TerminalDetector();
         boolean stdOutIsTerminal = terminalDetector.isSatisfiedBy(FileDescriptor.out);
         boolean stdErrIsTerminal = terminalDetector.isSatisfiedBy(FileDescriptor.err);
         if (stdOutIsTerminal) {
@@ -73,28 +73,12 @@ public class OutputEventRenderer implements OutputEventListener, LoggingConfigur
     }
 
     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);
-                }
-            }
-        });
+        addStandardOutputListener(new StreamBackedStandardOutputListener(out));
         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);
-                }
-            }
-        });
+        addStandardErrorListener(new StreamBackedStandardOutputListener(err));
         return this;
     }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/ProgressLogEventGenerator.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/ProgressLogEventGenerator.java
index 9d04cd5..acb2eb8 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/ProgressLogEventGenerator.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/ProgressLogEventGenerator.java
@@ -16,6 +16,8 @@
 package org.gradle.logging.internal;
 
 import org.gradle.api.logging.LogLevel;
+import org.gradle.util.GUtil;
+import org.gradle.util.SystemProperties;
 
 import java.util.LinkedList;
 
@@ -26,7 +28,8 @@ import static org.gradle.logging.StyledTextOutput.Style;
  * progress of operations.
  */
 public class ProgressLogEventGenerator implements OutputEventListener {
-    public static final String EOL = System.getProperty("line.separator");
+    private static final String EOL = SystemProperties.getLineSeparator();
+
     private final OutputEventListener listener;
     private final boolean deferHeader;
     private final LinkedList<Operation> operations = new LinkedList<Operation>();
@@ -64,14 +67,10 @@ public class ProgressLogEventGenerator implements OutputEventListener {
     }
 
     private void onStart(ProgressStartEvent progressStartEvent) {
-        Operation operation = new Operation();
-        operation.category = progressStartEvent.getCategory();
-        operation.description = progressStartEvent.getDescription();
-        operation.startTime = progressStartEvent.getTimestamp();
-        operation.status = "";
+        Operation operation = new Operation(progressStartEvent.getCategory(), progressStartEvent.getLoggingHeader(), progressStartEvent.getTimestamp());
         operations.add(operation);
 
-        if (!deferHeader) {
+        if (!deferHeader || !(progressStartEvent.getLoggingHeader() != null && progressStartEvent.getLoggingHeader().equals(progressStartEvent.getShortDescription()))) {
             operation.startHeader();
         }
     }
@@ -79,19 +78,19 @@ public class ProgressLogEventGenerator implements OutputEventListener {
     enum State {None, HeaderStarted, HeaderCompleted, Completed}
 
     private class Operation {
-        private String category;
-        private String description;
-        private String status;
+        private final String category;
+        private final String loggingHeader;
+        private final long startTime;
+        private final boolean hasLoggingHeader;
+        private String status = "";
         private State state = State.None;
-        public long startTime;
-        public long completeTime;
-
-        public String getDescription() {
-            return description;
-        }
+        private long completeTime;
 
-        public String getStatus() {
-            return status;
+        private Operation(String category, String loggingHeader, long startTime) {
+            this.category = category;
+            this.loggingHeader = loggingHeader;
+            this.startTime = startTime;
+            hasLoggingHeader = GUtil.isTrue(loggingHeader);
         }
 
         private void doOutput(RenderableOutputEvent event) {
@@ -106,21 +105,20 @@ public class ProgressLogEventGenerator implements OutputEventListener {
 
         public void startHeader() {
             assert state == State.None;
-            boolean hasDescription = description.length() > 0;
-            if (hasDescription) {
+            if (hasLoggingHeader) {
                 state = State.HeaderStarted;
-                doOutput(new StyledTextOutputEvent(startTime, category, LogLevel.LIFECYCLE, description));
+                doOutput(new StyledTextOutputEvent(startTime, category, LogLevel.LIFECYCLE, loggingHeader));
             } else {
                 state = State.HeaderCompleted;
             }
         }
 
         public void completeHeader() {
-            boolean hasDescription = description.length() > 0;
+            boolean hasDescription = GUtil.isTrue(loggingHeader);
             switch (state) {
                 case None:
                     if (hasDescription) {
-                        listener.onOutput(new StyledTextOutputEvent(startTime, category, LogLevel.LIFECYCLE, description + EOL));
+                        listener.onOutput(new StyledTextOutputEvent(startTime, category, LogLevel.LIFECYCLE, loggingHeader + EOL));
                     }
                     break;
                 case HeaderStarted:
@@ -135,24 +133,20 @@ public class ProgressLogEventGenerator implements OutputEventListener {
         }
 
         public void complete() {
-            boolean hasStatus = status.length() > 0;
-            boolean hasDescription = description.length() > 0;
+            boolean hasStatus = GUtil.isTrue(status);
             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) {
+                    if (hasLoggingHeader && hasStatus) {
                         doOutput(new StyledTextOutputEvent(completeTime, category, LogLevel.LIFECYCLE,
+                                new StyledTextOutputEvent.Span(loggingHeader + ' '),
                                 new StyledTextOutputEvent.Span(Style.ProgressStatus, status),
                                 new StyledTextOutputEvent.Span(EOL)));
+                    } else if (hasLoggingHeader) {
+                        doOutput(new StyledTextOutputEvent(completeTime, category, LogLevel.LIFECYCLE, loggingHeader + EOL));
                     }
                     break;
                 case HeaderStarted:
+                    assert hasLoggingHeader;
                     if (hasStatus) {
                         doOutput(new StyledTextOutputEvent(completeTime, category, LogLevel.LIFECYCLE,
                                 new StyledTextOutputEvent.Span(" "),
@@ -163,13 +157,9 @@ public class ProgressLogEventGenerator implements OutputEventListener {
                     }
                     break;
                 case HeaderCompleted:
-                    if (hasDescription && hasStatus) {
+                    if (hasLoggingHeader && 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(loggingHeader + ' '),
                                 new StyledTextOutputEvent.Span(Style.ProgressStatus, status),
                                 new StyledTextOutputEvent.Span(EOL)));
                     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/ProgressStartEvent.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/ProgressStartEvent.java
index 86d7f1c..0e836f1 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/ProgressStartEvent.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/ProgressStartEvent.java
@@ -19,16 +19,34 @@ import org.gradle.api.logging.LogLevel;
 
 public class ProgressStartEvent extends CategorisedOutputEvent {
     private final String description;
+    private final String shortDescription;
+    private final String loggingHeader;
+    private final String status;
 
-    public ProgressStartEvent(long timestamp, String category, String description) {
+    public ProgressStartEvent(long timestamp, String category, String description, String shortDescription, String loggingHeader, String status) {
         super(timestamp, category, LogLevel.LIFECYCLE);
         this.description = description;
+        this.shortDescription = shortDescription;
+        this.loggingHeader = loggingHeader;
+        this.status = status;
     }
 
     public String getDescription() {
         return description;
     }
 
+    public String getShortDescription() {
+        return shortDescription;
+    }
+
+    public String getLoggingHeader() {
+        return loggingHeader;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
     @Override
     public String toString() {
         return String.format("ProgressStart %s", description);
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/StreamBackedStandardOutputListener.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/StreamBackedStandardOutputListener.java
new file mode 100644
index 0000000..e838530
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/StreamBackedStandardOutputListener.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.*;
+
+public class StreamBackedStandardOutputListener implements StandardOutputListener {
+    private final Appendable appendable;
+    private final Flushable flushable;
+
+    public StreamBackedStandardOutputListener(Appendable appendable) {
+        this.appendable = appendable;
+        if (appendable instanceof Flushable) {
+            flushable = (Flushable) appendable;
+        } else {
+            flushable = new Flushable() {
+                public void flush() throws IOException {
+                }
+            };
+        }
+    }
+
+    public StreamBackedStandardOutputListener(OutputStream outputStream) {
+        this(new OutputStreamWriter(outputStream));
+    }
+
+    public void onOutput(CharSequence output) {
+        try {
+            appendable.append(output);
+            flushable.flush();
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/StreamingStyledTextOutput.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/StreamingStyledTextOutput.java
index aa83214..f217b50 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/StreamingStyledTextOutput.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/StreamingStyledTextOutput.java
@@ -15,7 +15,6 @@
  */
 package org.gradle.logging.internal;
 
-import org.gradle.api.UncheckedIOException;
 import org.gradle.api.logging.StandardOutputListener;
 
 import java.io.Closeable;
@@ -42,15 +41,7 @@ public class StreamingStyledTextOutput extends AbstractStyledTextOutput implemen
      * @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);
-                }
-            }
-        });
+        this(appendable, new StreamBackedStandardOutputListener(appendable));
     }
 
     private StreamingStyledTextOutput(Object target, StandardOutputListener listener) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/StyledTextOutputBackedRenderer.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/StyledTextOutputBackedRenderer.java
index 49697e0..3e2d01a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/StyledTextOutputBackedRenderer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/StyledTextOutputBackedRenderer.java
@@ -17,6 +17,7 @@ package org.gradle.logging.internal;
 
 import org.gradle.api.logging.LogLevel;
 import org.gradle.logging.StyledTextOutput;
+import org.gradle.util.SystemProperties;
 
 import java.text.SimpleDateFormat;
 import java.util.Date;
@@ -25,7 +26,8 @@ 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 static final String EOL = SystemProperties.getLineSeparator();
+
     private final OutputEventTextOutputImpl textOutput;
     private boolean debugOutput;
     private RenderableOutputEvent lastEvent;
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/TerminalDetector.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/TerminalDetector.java
index 2c1c8ba..62fa544 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/TerminalDetector.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/TerminalDetector.java
@@ -16,21 +16,55 @@
 
 package org.gradle.logging.internal;
 
+import org.apache.commons.io.IOUtils;
 import org.fusesource.jansi.WindowsAnsiOutputStream;
+import org.gradle.api.GradleException;
+import org.gradle.api.UncheckedIOException;
 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;
+import java.io.*;
 
 public class TerminalDetector implements Spec<FileDescriptor> {
+    public TerminalDetector(File libCacheDir) {
+        // Some hackery to prevent JNA from creating a shared lib in the tmp dir, as it does not clean things up
+        File tmpDir = new File(libCacheDir, "jna");
+        tmpDir.mkdirs();
+        String libName = System.mapLibraryName("jnidispatch");
+        File libFile = new File(tmpDir, libName);
+        if (!libFile.exists()) {
+            String resourceName = "/com/sun/jna/" + OperatingSystem.current().getNativePrefix() + "/" + libName;
+            try {
+                InputStream lib = getClass().getResourceAsStream(resourceName);
+                if (lib == null) {
+                    throw new GradleException(String.format("Could not locate JNA native lib resource '%s'.", resourceName));
+                }
+                try {
+                    FileOutputStream outputStream = new FileOutputStream(libFile);
+                    try {
+                        IOUtils.copy(lib, outputStream);
+                    } finally {
+                        outputStream.close();
+                    }
+                } finally {
+                    lib.close();
+                }
+            } catch (IOException e) {
+                throw new UncheckedIOException(e);
+            }
+        }
+//        System.load(libFile.getAbsolutePath());
+        System.setProperty("jna.boot.library.path", tmpDir.getAbsolutePath());
+    }
+
     public boolean isSatisfiedBy(FileDescriptor element) {
+
         if (OperatingSystem.current().isWindows()) {
             // Use Jansi's detection mechanism
             try {
                 new WindowsAnsiOutputStream(new ByteArrayOutputStream());
+                return true;
             } catch (IOException ignore) {
                 // Not attached to a console
                 return false;
diff --git a/subprojects/core/src/main/groovy/org/gradle/messaging/actor/internal/DefaultActorFactory.java b/subprojects/core/src/main/groovy/org/gradle/messaging/actor/internal/DefaultActorFactory.java
index 13084dc..5d91fac 100644
--- a/subprojects/core/src/main/groovy/org/gradle/messaging/actor/internal/DefaultActorFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/messaging/actor/internal/DefaultActorFactory.java
@@ -37,6 +37,9 @@ public class DefaultActorFactory implements ActorFactory, Stoppable {
         this.executorFactory = executorFactory;
     }
 
+    /**
+     * Stops all actors.
+     */
     public void stop() {
         synchronized (lock) {
             try {
diff --git a/subprojects/core/src/main/groovy/org/gradle/messaging/remote/internal/SocketConnection.java b/subprojects/core/src/main/groovy/org/gradle/messaging/remote/internal/SocketConnection.java
index 64cde17..4a0c68d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/messaging/remote/internal/SocketConnection.java
+++ b/subprojects/core/src/main/groovy/org/gradle/messaging/remote/internal/SocketConnection.java
@@ -73,7 +73,7 @@ public class SocketConnection<T> implements Connection<T> {
         if (e instanceof EOFException) {
             return true;
         }
-        if (e instanceof IOException && e.getMessage().equals("An existing connection was forcibly closed by the remote host")) {
+        if (e instanceof IOException && e.getMessage() != null && e.getMessage().equals("An existing connection was forcibly closed by the remote host")) {
             return true;
         }
         return false;
diff --git a/subprojects/core/src/main/groovy/org/gradle/messaging/remote/internal/TcpIncomingConnector.java b/subprojects/core/src/main/groovy/org/gradle/messaging/remote/internal/TcpIncomingConnector.java
index 615d5c2..cc50fc0 100644
--- a/subprojects/core/src/main/groovy/org/gradle/messaging/remote/internal/TcpIncomingConnector.java
+++ b/subprojects/core/src/main/groovy/org/gradle/messaging/remote/internal/TcpIncomingConnector.java
@@ -94,6 +94,8 @@ public class TcpIncomingConnector implements IncomingConnector, AsyncStoppable {
                         InetSocketAddress remoteAddress = (InetSocketAddress) socket.socket().getRemoteSocketAddress();
                         if (!localAddresses.contains(remoteAddress.getAddress())) {
                             LOGGER.error("Cannot accept connection from remote address {}.", remoteAddress.getAddress());
+                            socket.close();
+                            continue;
                         }
                         URI remoteUri = new URI(String.format("tcp://localhost:%d", remoteAddress.getPort()));
                         LOGGER.debug("Accepted connection from {}.", remoteUri);
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/BaseExecSpec.java b/subprojects/core/src/main/groovy/org/gradle/process/BaseExecSpec.java
index e45485c..734401a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/BaseExecSpec.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/BaseExecSpec.java
@@ -26,17 +26,17 @@ import java.util.List;
  */
 public interface BaseExecSpec extends ProcessForkOptions {
     /**
-     * Sets whether an exit value different from zero should be ignored. In case it is not ignored, an exception is
-     * thrown in case of such an exit value.
+     * Sets whether a non-zero exit value is ignored, or an exception thrown.
      *
-     * @param ignoreExitValue whether to ignore the exit value or not
+     * @param ignoreExitValue whether a non-zero exit value is ignored, or an exception thrown
      * @return this
      */
     BaseExecSpec setIgnoreExitValue(boolean ignoreExitValue);
 
     /**
-     * Specifies whether an exit value different from zero should be ignored. In case it is not ignored, an exception is
-     * thrown in case of such an exit value. Defaults to <code>false</code>.
+     * Tells whether a non-zero exit value is ignored, or an exception thrown. Defaults to <code>false</code>.
+     *
+     * @return whether a non-zero exit value is ignored, or an exception thrown
      */
     boolean isIgnoreExitValue();
 
@@ -93,6 +93,8 @@ public interface BaseExecSpec extends ProcessForkOptions {
 
     /**
      * Returns the full command line, including the executable plus its arguments.
+     *
+     * @return The full command line, including the executable plus its arguments
      */
     List<String> getCommandLine();
 }
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/ExecResult.java b/subprojects/core/src/main/groovy/org/gradle/process/ExecResult.java
index fd43fa0..df3ef6a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/ExecResult.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/ExecResult.java
@@ -19,21 +19,21 @@ package org.gradle.process;
 import org.gradle.process.internal.ExecException;
 
 /**
- * Represent the result of running an external process.
+ * Represents the result of running an external process.
  *
  * @author Hans Dockter
  */
 public interface ExecResult {
     /**
-     * Return the exit value of the process.
+     * Returns the exit value of the process.
      */
     int getExitValue();
 
     /**
-     * Throws an {@link org.gradle.process.internal.ExecException} if the process did not exit with a 0 exit value.
+     * Throws an {@link org.gradle.process.internal.ExecException} if the process exited with a non-zero exit value.
      *
      * @return this
-     * @throws ExecException On non-zero exit value.
+     * @throws ExecException if the process exited with a non-zero exit value
      */
     ExecResult assertNormalExitValue() throws ExecException;
 
@@ -41,7 +41,7 @@ public interface ExecResult {
      * Re-throws any failure executing this process.
      *
      * @return this
-     * @throws ExecException The execution failure
+     * @throws ExecException the execution failure
      */
     ExecResult rethrowFailure() throws ExecException;
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultExecHandle.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultExecHandle.java
index 9e142c6..77998e0 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultExecHandle.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultExecHandle.java
@@ -332,7 +332,7 @@ public class DefaultExecHandle implements ExecHandle {
 
         public ExecResult assertNormalExitValue() throws ExecException {
             if (exitValue != 0) {
-                throw new ExecException(String.format("%s finished with non-zero exit value.", StringUtils.capitalize(displayName)));
+                throw new ExecException(String.format("%s finished with (non-zero) exit value %d.", StringUtils.capitalize(displayName), exitValue));
             }
             return this;
         }
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultJavaForkOptions.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultJavaForkOptions.java
index c739abf..e44fb33 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultJavaForkOptions.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultJavaForkOptions.java
@@ -1,228 +1,228 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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;
-
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.internal.file.PathResolvingFileCollection;
-import org.gradle.process.JavaForkOptions;
-import org.gradle.util.Jvm;
-
-import java.io.File;
-import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class DefaultJavaForkOptions extends DefaultProcessForkOptions implements JavaForkOptions {
-    private final Pattern sysPropPattern = Pattern.compile("-D(.+?)=(.*)");
-    private final Pattern noArgSysPropPattern = Pattern.compile("-D([^=]+)");
-    private final Pattern maxHeapPattern = Pattern.compile("-Xmx(.+)");
-    private final Pattern bootstrapPattern = Pattern.compile("-Xbootclasspath:(.+)");
-    private final List<Object> extraJvmArgs = new ArrayList<Object>();
-    private final Map<String, Object> systemProperties = new TreeMap<String, Object>();
-    private FileCollection bootstrapClasspath;
-    private String maxHeapSize;
-    private boolean assertionsEnabled;
-    private boolean debug;
-
-    public DefaultJavaForkOptions(FileResolver resolver) {
-        this(resolver, Jvm.current());
-    }
-
-    public DefaultJavaForkOptions(FileResolver resolver, Jvm jvm) {
-        super(resolver);
-        this.bootstrapClasspath = new PathResolvingFileCollection(resolver, null);
-        setExecutable(jvm.getJavaExecutable());
-    }
-
-    public List<String> getAllJvmArgs() {
-        List<String> args = new ArrayList<String>();
-        args.addAll(getJvmArgs());
-        for (Map.Entry<String, Object> entry : getSystemProperties().entrySet()) {
-            if (entry.getValue() != null) {
-                args.add(String.format("-D%s=%s", entry.getKey(), entry.getValue().toString()));
-            } else {
-                args.add(String.format("-D%s", entry.getKey()));
-            }
-        }
-        if (maxHeapSize != null) {
-            args.add(String.format("-Xmx%s", maxHeapSize));
-        }
-        FileCollection bootstrapClasspath = getBootstrapClasspath();
-        if (!bootstrapClasspath.isEmpty()) {
-            args.add(String.format("-Xbootclasspath:%s", bootstrapClasspath.getAsPath()));
-        }
-        if (assertionsEnabled) {
-            args.add("-ea");
-        }
-        if (debug) {
-            args.add("-Xdebug");
-            args.add("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005");
-        }
-        return args;
-    }
-
-    public void setAllJvmArgs(Iterable<?> arguments) {
-        systemProperties.clear();
-        maxHeapSize = null;
-        extraJvmArgs.clear();
-        assertionsEnabled = false;
-        jvmArgs(arguments);
-    }
-
-    public List<String> getJvmArgs() {
-        List<String> args = new ArrayList<String>();
-        for (Object extraJvmArg : extraJvmArgs) {
-            args.add(extraJvmArg.toString());
-        }
-        return args;
-    }
-
-    public void setJvmArgs(Iterable<?> arguments) {
-        extraJvmArgs.clear();
-        jvmArgs(arguments);
-    }
-
-    public JavaForkOptions jvmArgs(Iterable<?> arguments) {
-        for (Object argument : arguments) {
-            String argStr = argument.toString();
-            Matcher matcher = sysPropPattern.matcher(argStr);
-            if (matcher.matches()) {
-                systemProperties.put(matcher.group(1), matcher.group(2));
-                continue;
-            }
-            matcher = noArgSysPropPattern.matcher(argStr);
-            if (matcher.matches()) {
-                systemProperties.put(matcher.group(1), null);
-                continue;
-            }
-            matcher = maxHeapPattern.matcher(argStr);
-            if (matcher.matches()) {
-                maxHeapSize = matcher.group(1);
-                continue;
-            }
-            matcher = bootstrapPattern.matcher(argStr);
-            if (matcher.matches()) {
-                setBootstrapClasspath(getResolver().resolveFiles((Object[]) matcher.group(1).split(Pattern.quote(File.pathSeparator))));
-                continue;
-            }
-            if (argStr.equals("-ea") || argStr.equals("-enableassertions")) {
-                assertionsEnabled = true;
-                continue;
-            }
-            if (argStr.equals("-da") || argStr.equals("-disableassertions")) {
-                assertionsEnabled = false;
-                continue;
-            }
-
-            extraJvmArgs.add(argument);
-        }
-
-        boolean xdebugFound = false;
-        boolean xrunjdwpFound = false;
-        Set<Object> matches = new HashSet<Object>();
-        for (Object extraJvmArg : extraJvmArgs) {
-            if (extraJvmArg.toString().equals("-Xdebug")) {
-                xdebugFound = true;
-                matches.add(extraJvmArg);
-            } else if (extraJvmArg.toString().equals("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005")) {
-                xrunjdwpFound = true;
-                matches.add(extraJvmArg);
-            }
-        }
-        if (xdebugFound && xrunjdwpFound) {
-            debug = true;
-            extraJvmArgs.removeAll(matches);
-        } else {
-            debug = false;
-        }
-
-        return this;
-    }
-
-    public JavaForkOptions jvmArgs(Object... arguments) {
-        jvmArgs(Arrays.asList(arguments));
-        return this;
-    }
-
-    public Map<String, Object> getSystemProperties() {
-        return systemProperties;
-    }
-
-    public void setSystemProperties(Map<String, ?> properties) {
-        systemProperties.clear();
-        systemProperties.putAll(properties);
-    }
-
-    public JavaForkOptions systemProperties(Map<String, ?> properties) {
-        systemProperties.putAll(properties);
-        return this;
-    }
-
-    public JavaForkOptions systemProperty(String name, Object value) {
-        systemProperties.put(name, value);
-        return this;
-    }
-
-    public FileCollection getBootstrapClasspath() {
-        return bootstrapClasspath;
-    }
-
-    public void setBootstrapClasspath(FileCollection classpath) {
-        this.bootstrapClasspath = classpath;
-    }
-
-    public JavaForkOptions bootstrapClasspath(Object... classpath) {
-        this.bootstrapClasspath = this.bootstrapClasspath.plus(getResolver().resolveFiles(classpath));
-        return this;
-    }
-
-    public String getMaxHeapSize() {
-        return maxHeapSize;
-    }
-
-    public void setMaxHeapSize(String heapSize) {
-        this.maxHeapSize = heapSize;
-    }
-
-    public boolean getEnableAssertions() {
-        return assertionsEnabled;
-    }
-
-    public void setEnableAssertions(boolean enabled) {
-        assertionsEnabled = enabled;
-    }
-
-    public boolean getDebug() {
-        return debug;
-    }
-
-    public void setDebug(boolean enabled) {
-        debug = enabled;
-    }
-
-    public JavaForkOptions copyTo(JavaForkOptions target) {
-        super.copyTo(target);
-        target.setJvmArgs(extraJvmArgs);
-        target.setSystemProperties(systemProperties);
-        target.setMaxHeapSize(maxHeapSize);
-        target.setBootstrapClasspath(bootstrapClasspath);
-        target.setEnableAssertions(assertionsEnabled);
-        target.setDebug(debug);
-        return this;
-    }
-}
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection;
+import org.gradle.process.JavaForkOptions;
+import org.gradle.util.Jvm;
+
+import java.io.File;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class DefaultJavaForkOptions extends DefaultProcessForkOptions implements JavaForkOptions {
+    private final Pattern sysPropPattern = Pattern.compile("-D(.+?)=(.*)");
+    private final Pattern noArgSysPropPattern = Pattern.compile("-D([^=]+)");
+    private final Pattern maxHeapPattern = Pattern.compile("-Xmx(.+)");
+    private final Pattern bootstrapPattern = Pattern.compile("-Xbootclasspath:(.+)");
+    private final List<Object> extraJvmArgs = new ArrayList<Object>();
+    private final Map<String, Object> systemProperties = new TreeMap<String, Object>();
+    private FileCollection bootstrapClasspath;
+    private String maxHeapSize;
+    private boolean assertionsEnabled;
+    private boolean debug;
+
+    public DefaultJavaForkOptions(FileResolver resolver) {
+        this(resolver, Jvm.current());
+    }
+
+    public DefaultJavaForkOptions(FileResolver resolver, Jvm jvm) {
+        super(resolver);
+        this.bootstrapClasspath = new DefaultConfigurableFileCollection(resolver, null);
+        setExecutable(jvm.getJavaExecutable());
+    }
+
+    public List<String> getAllJvmArgs() {
+        List<String> args = new ArrayList<String>();
+        args.addAll(getJvmArgs());
+        for (Map.Entry<String, Object> entry : getSystemProperties().entrySet()) {
+            if (entry.getValue() != null) {
+                args.add(String.format("-D%s=%s", entry.getKey(), entry.getValue().toString()));
+            } else {
+                args.add(String.format("-D%s", entry.getKey()));
+            }
+        }
+        if (maxHeapSize != null) {
+            args.add(String.format("-Xmx%s", maxHeapSize));
+        }
+        FileCollection bootstrapClasspath = getBootstrapClasspath();
+        if (!bootstrapClasspath.isEmpty()) {
+            args.add(String.format("-Xbootclasspath:%s", bootstrapClasspath.getAsPath()));
+        }
+        if (assertionsEnabled) {
+            args.add("-ea");
+        }
+        if (debug) {
+            args.add("-Xdebug");
+            args.add("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005");
+        }
+        return args;
+    }
+
+    public void setAllJvmArgs(Iterable<?> arguments) {
+        systemProperties.clear();
+        maxHeapSize = null;
+        extraJvmArgs.clear();
+        assertionsEnabled = false;
+        jvmArgs(arguments);
+    }
+
+    public List<String> getJvmArgs() {
+        List<String> args = new ArrayList<String>();
+        for (Object extraJvmArg : extraJvmArgs) {
+            args.add(extraJvmArg.toString());
+        }
+        return args;
+    }
+
+    public void setJvmArgs(Iterable<?> arguments) {
+        extraJvmArgs.clear();
+        jvmArgs(arguments);
+    }
+
+    public JavaForkOptions jvmArgs(Iterable<?> arguments) {
+        for (Object argument : arguments) {
+            String argStr = argument.toString();
+            Matcher matcher = sysPropPattern.matcher(argStr);
+            if (matcher.matches()) {
+                systemProperties.put(matcher.group(1), matcher.group(2));
+                continue;
+            }
+            matcher = noArgSysPropPattern.matcher(argStr);
+            if (matcher.matches()) {
+                systemProperties.put(matcher.group(1), null);
+                continue;
+            }
+            matcher = maxHeapPattern.matcher(argStr);
+            if (matcher.matches()) {
+                maxHeapSize = matcher.group(1);
+                continue;
+            }
+            matcher = bootstrapPattern.matcher(argStr);
+            if (matcher.matches()) {
+                setBootstrapClasspath(getResolver().resolveFiles((Object[]) matcher.group(1).split(Pattern.quote(File.pathSeparator))));
+                continue;
+            }
+            if (argStr.equals("-ea") || argStr.equals("-enableassertions")) {
+                assertionsEnabled = true;
+                continue;
+            }
+            if (argStr.equals("-da") || argStr.equals("-disableassertions")) {
+                assertionsEnabled = false;
+                continue;
+            }
+
+            extraJvmArgs.add(argument);
+        }
+
+        boolean xdebugFound = false;
+        boolean xrunjdwpFound = false;
+        Set<Object> matches = new HashSet<Object>();
+        for (Object extraJvmArg : extraJvmArgs) {
+            if (extraJvmArg.toString().equals("-Xdebug")) {
+                xdebugFound = true;
+                matches.add(extraJvmArg);
+            } else if (extraJvmArg.toString().equals("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005")) {
+                xrunjdwpFound = true;
+                matches.add(extraJvmArg);
+            }
+        }
+        if (xdebugFound && xrunjdwpFound) {
+            debug = true;
+            extraJvmArgs.removeAll(matches);
+        } else {
+            debug = false;
+        }
+
+        return this;
+    }
+
+    public JavaForkOptions jvmArgs(Object... arguments) {
+        jvmArgs(Arrays.asList(arguments));
+        return this;
+    }
+
+    public Map<String, Object> getSystemProperties() {
+        return systemProperties;
+    }
+
+    public void setSystemProperties(Map<String, ?> properties) {
+        systemProperties.clear();
+        systemProperties.putAll(properties);
+    }
+
+    public JavaForkOptions systemProperties(Map<String, ?> properties) {
+        systemProperties.putAll(properties);
+        return this;
+    }
+
+    public JavaForkOptions systemProperty(String name, Object value) {
+        systemProperties.put(name, value);
+        return this;
+    }
+
+    public FileCollection getBootstrapClasspath() {
+        return bootstrapClasspath;
+    }
+
+    public void setBootstrapClasspath(FileCollection classpath) {
+        this.bootstrapClasspath = classpath;
+    }
+
+    public JavaForkOptions bootstrapClasspath(Object... classpath) {
+        this.bootstrapClasspath = this.bootstrapClasspath.plus(getResolver().resolveFiles(classpath));
+        return this;
+    }
+
+    public String getMaxHeapSize() {
+        return maxHeapSize;
+    }
+
+    public void setMaxHeapSize(String heapSize) {
+        this.maxHeapSize = heapSize;
+    }
+
+    public boolean getEnableAssertions() {
+        return assertionsEnabled;
+    }
+
+    public void setEnableAssertions(boolean enabled) {
+        assertionsEnabled = enabled;
+    }
+
+    public boolean getDebug() {
+        return debug;
+    }
+
+    public void setDebug(boolean enabled) {
+        debug = enabled;
+    }
+
+    public JavaForkOptions copyTo(JavaForkOptions target) {
+        super.copyTo(target);
+        target.setJvmArgs(extraJvmArgs);
+        target.setSystemProperties(systemProperties);
+        target.setMaxHeapSize(maxHeapSize);
+        target.setBootstrapClasspath(bootstrapClasspath);
+        target.setEnableAssertions(assertionsEnabled);
+        target.setDebug(debug);
+        return this;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultWorkerProcess.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultWorkerProcess.java
index 39b3ce1..ba555fd 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultWorkerProcess.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultWorkerProcess.java
@@ -37,11 +37,7 @@ public class DefaultWorkerProcess implements WorkerProcess {
     private Throwable processFailure;
     private final long connectTimeout;
 
-    public DefaultWorkerProcess() {
-        this(30, TimeUnit.SECONDS);
-    }
-
-    DefaultWorkerProcess(int connectTimeoutValue, TimeUnit connectTimeoutUnits) {
+    public DefaultWorkerProcess(int connectTimeoutValue, TimeUnit connectTimeoutUnits) {
         connectTimeout = connectTimeoutUnits.toMillis(connectTimeoutValue);
     }
 
@@ -80,7 +76,7 @@ public class DefaultWorkerProcess implements WorkerProcess {
         try {
             try {
                 execResult.rethrowFailure().assertNormalExitValue();
-            } catch (ExecException e) {
+            } catch (Throwable e) {
                 processFailure = e;
             }
             running = false;
@@ -139,6 +135,8 @@ public class DefaultWorkerProcess implements WorkerProcess {
         try {
             connection = this.connection;
         } finally {
+            this.connection = null;
+            this.execHandle = null;
             lock.unlock();
         }
         if (connection != null) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultWorkerProcessFactory.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultWorkerProcessFactory.java
index 7ad2f23..d7e4329 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultWorkerProcessFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultWorkerProcessFactory.java
@@ -36,6 +36,7 @@ import java.net.URI;
 import java.net.URL;
 import java.util.List;
 import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
 
 public class DefaultWorkerProcessFactory implements Factory<WorkerProcessBuilder> {
     private static final Logger LOGGER = LoggerFactory.getLogger(DefaultWorkerProcessFactory.class);
@@ -72,9 +73,10 @@ public class DefaultWorkerProcessFactory implements Factory<WorkerProcessBuilder
                 throw new IllegalStateException("No worker action specified for this worker process.");
             }
 
-            final DefaultWorkerProcess workerProcess = new DefaultWorkerProcess();
+            final DefaultWorkerProcess workerProcess = new DefaultWorkerProcess(120, TimeUnit.SECONDS);
             URI localAddress = server.accept(workerProcess.getConnectAction());
 
+            // Build configuration for GradleWorkerMain
             List<URL> implementationClassPath = ClasspathUtil.getClasspath(getWorker().getClass().getClassLoader());
             Object id = idGenerator.generateId();
             String displayName = String.format("Gradle Worker %s", id);
@@ -88,18 +90,17 @@ public class DefaultWorkerProcessFactory implements Factory<WorkerProcessBuilder
                         implementationClassPath, localAddress, classPathRegistry);
             }
             Callable<?> workerMain = workerFactory.create();
-            getJavaCommand().classpath(workerFactory.getSystemClasspath());
-
-            // Build configuration for GradleWorkerMain
             byte[] config = GUtil.serialize(workerMain);
 
             LOGGER.debug("Creating {}", displayName);
             LOGGER.debug("Using application classpath {}", getApplicationClasspath());
             LOGGER.debug("Using implementation classpath {}", implementationClassPath);
 
-            getJavaCommand().setStandardInput(new ByteArrayInputStream(config));
-            getJavaCommand().setDisplayName(displayName);
-            ExecHandle execHandle = getJavaCommand().build();
+            JavaExecHandleBuilder javaCommand = getJavaCommand();
+            javaCommand.classpath(workerFactory.getSystemClasspath());
+            javaCommand.setStandardInput(new ByteArrayInputStream(config));
+            javaCommand.setDisplayName(displayName);
+            ExecHandle execHandle = javaCommand.build();
 
             workerProcess.setExecHandle(execHandle);
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecHandle.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecHandle.java
index 3e2c4c4..741509c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecHandle.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecHandle.java
@@ -35,6 +35,11 @@ public interface ExecHandle {
 
     Map<String, String> getEnvironment();
 
+    /**
+     * Starts this process, blocking until the process has started.
+     *
+     * @return this
+     */
     ExecHandle start();
 
     void abort();
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/JavaExecHandleBuilder.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/JavaExecHandleBuilder.java
index d767d2a..ec4e155 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/JavaExecHandleBuilder.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/JavaExecHandleBuilder.java
@@ -16,8 +16,8 @@
 package org.gradle.process.internal;
 
 import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection;
 import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.internal.file.PathResolvingFileCollection;
 import org.gradle.process.JavaExecSpec;
 import org.gradle.process.JavaForkOptions;
 import org.gradle.util.GUtil;
@@ -39,7 +39,7 @@ public class JavaExecHandleBuilder extends AbstractExecHandleBuilder implements
         super(fileResolver);
         this.fileResolver = fileResolver;
         javaOptions = new DefaultJavaForkOptions(fileResolver);
-        classpath = new PathResolvingFileCollection(fileResolver, null);
+        classpath = new DefaultConfigurableFileCollection(fileResolver, null);
         executable(javaOptions.getExecutable());
     }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/child/ImplementationClassLoaderWorker.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/child/ImplementationClassLoaderWorker.java
index 61b068a..1774873 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/child/ImplementationClassLoaderWorker.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/child/ImplementationClassLoaderWorker.java
@@ -80,7 +80,7 @@ public class ImplementationClassLoaderWorker implements Action<WorkerContext>, S
     }
 
     LoggingManagerInternal createLoggingManager() {
-        return new LoggingServiceRegistry().newInstance(LoggingManagerInternal.class);
+        return LoggingServiceRegistry.newChildProcessLogging().newInstance(LoggingManagerInternal.class);
     }
 
     ObservableUrlClassLoader createImplementationClassLoader(ClassLoader system, ClassLoader application) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/testfixtures/ProjectBuilder.java b/subprojects/core/src/main/groovy/org/gradle/testfixtures/ProjectBuilder.java
index bc45b12..7ac7f7c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/testfixtures/ProjectBuilder.java
+++ b/subprojects/core/src/main/groovy/org/gradle/testfixtures/ProjectBuilder.java
@@ -18,28 +18,16 @@ package org.gradle.testfixtures;
 import org.gradle.StartParameter;
 import org.gradle.api.Project;
 import org.gradle.api.UncheckedIOException;
-import org.gradle.api.internal.*;
-import org.gradle.api.internal.project.*;
-import org.gradle.api.logging.LogLevel;
-import org.gradle.api.logging.LoggingManager;
-import org.gradle.api.logging.StandardOutputListener;
-import org.gradle.cache.AutoCloseCacheFactory;
-import org.gradle.cache.CacheFactory;
-import org.gradle.cache.DefaultCacheFactory;
-import org.gradle.configuration.GradleLauncherMetaData;
-import org.gradle.initialization.*;
+import org.gradle.api.internal.GradleInternal;
+import org.gradle.api.internal.project.IProjectFactory;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.project.ServiceRegistryFactory;
+import org.gradle.initialization.DefaultProjectDescriptor;
+import org.gradle.initialization.DefaultProjectDescriptorRegistry;
 import org.gradle.invocation.DefaultGradle;
-import org.gradle.listener.DefaultListenerManager;
-import org.gradle.listener.ListenerManager;
-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.testfixtures.internal.GlobalTestServices;
+import org.gradle.testfixtures.internal.TestTopLevelBuildServiceRegistry;
 import org.gradle.util.GFileUtils;
-import org.gradle.util.TrueTimeProvider;
 
 import java.io.File;
 import java.io.IOException;
@@ -108,7 +96,7 @@ public class ProjectBuilder {
         StartParameter startParameter = new StartParameter();
         startParameter.setGradleUserHomeDir(new File(projectDir, "userHome"));
 
-        ServiceRegistryFactory topLevelRegistry = new TestTopLevelBuildServiceRegistry(startParameter, homeDir);
+        ServiceRegistryFactory topLevelRegistry = new TestTopLevelBuildServiceRegistry(GLOBAL_SERVICES, startParameter, homeDir);
         GradleInternal gradle = new DefaultGradle(null, startParameter, topLevelRegistry);
 
         DefaultProjectDescriptor projectDescriptor = new DefaultProjectDescriptor(null, "test", projectDir, new DefaultProjectDescriptorRegistry());
@@ -120,125 +108,4 @@ public class ProjectBuilder {
         return project;
     }
 
-    private static class NoOpLoggingManager implements LoggingManagerInternal {
-        private LogLevel stdoutLevel = LogLevel.LIFECYCLE;
-
-        public LoggingManagerInternal captureStandardOutput(LogLevel level) {
-            stdoutLevel = level;
-            return this;
-        }
-
-        public LoggingManager disableStandardOutputCapture() {
-            stdoutLevel = null;
-            return this;
-        }
-
-        public boolean isStandardOutputCaptureEnabled() {
-            return stdoutLevel != null;
-        }
-
-        public LogLevel getStandardOutputCaptureLevel() {
-            return stdoutLevel;
-        }
-
-        public LoggingManagerInternal captureStandardError(LogLevel level) {
-            return this;
-        }
-
-        public LoggingManagerInternal setLevel(LogLevel logLevel) {
-            return this;
-        }
-
-        public LogLevel getStandardErrorCaptureLevel() {
-            return LogLevel.ERROR;
-        }
-
-        public LoggingManagerInternal start() {
-            return this;
-        }
-
-        public LoggingManagerInternal stop() {
-            return this;
-        }
-
-        public void addStandardErrorListener(StandardOutputListener listener) {
-        }
-
-        public void addStandardOutputListener(StandardOutputListener listener) {
-        }
-
-        public void removeStandardOutputListener(StandardOutputListener listener) {
-        }
-
-        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 {
-        protected ListenerManager createListenerManager() {
-            return new DefaultListenerManager();
-        }
-
-        protected ClassPathRegistry createClassPathRegistry() {
-            return new DefaultClassPathRegistry();
-        }
-
-        protected ClassLoaderFactory createClassLoaderFactory() {
-            return new DefaultClassLoaderFactory(get(ClassPathRegistry.class));
-        }
-
-        protected CacheFactory createCacheFactory() {
-            return new AutoCloseCacheFactory(new DefaultCacheFactory());
-        }
-
-        protected ProgressLoggerFactory createProgressLoggerFactory() {
-            return new DefaultProgressLoggerFactory(get(ListenerManager.class).getBroadcaster(ProgressListener.class), new TrueTimeProvider());
-        }
-
-        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));
-        }
-    }
-
-    private static class TestTopLevelBuildServiceRegistry extends TopLevelBuildServiceRegistry {
-        private final File homeDir;
-
-        public TestTopLevelBuildServiceRegistry(StartParameter startParameter, File homeDir) {
-            super(ProjectBuilder.GLOBAL_SERVICES, startParameter);
-            this.homeDir = homeDir;
-        }
-
-        protected BuildClientMetaData createClientMetaData() {
-            return new GradleLauncherMetaData();
-        }
-
-        protected GradleDistributionLocator createGradleDistributionLocator() {
-            return new GradleDistributionLocator() {
-                public File getGradleHome() {
-                    return homeDir;
-                }
-            };
-        }
-    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/GlobalTestServices.java b/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/GlobalTestServices.java
new file mode 100644
index 0000000..8af68d1
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/GlobalTestServices.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.testfixtures.internal;
+
+import org.gradle.api.internal.ClassPathRegistry;
+import org.gradle.api.internal.DefaultClassPathRegistry;
+import org.gradle.api.internal.Factory;
+import org.gradle.api.internal.project.DefaultIsolatedAntBuilder;
+import org.gradle.api.internal.project.DefaultServiceRegistry;
+import org.gradle.api.internal.project.IsolatedAntBuilder;
+import org.gradle.cache.AutoCloseCacheFactory;
+import org.gradle.cache.CacheFactory;
+import org.gradle.initialization.ClassLoaderFactory;
+import org.gradle.initialization.DefaultClassLoaderFactory;
+import org.gradle.listener.DefaultListenerManager;
+import org.gradle.listener.ListenerManager;
+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.TrueTimeProvider;
+
+public class GlobalTestServices extends DefaultServiceRegistry {
+    protected ListenerManager createListenerManager() {
+        return new DefaultListenerManager();
+    }
+
+    protected ClassPathRegistry createClassPathRegistry() {
+        return new DefaultClassPathRegistry();
+    }
+
+    protected ClassLoaderFactory createClassLoaderFactory() {
+        return new DefaultClassLoaderFactory(get(ClassPathRegistry.class));
+    }
+
+    protected CacheFactory createCacheFactory() {
+        return new AutoCloseCacheFactory(new InMemoryCacheFactory());
+    }
+
+    protected ProgressLoggerFactory createProgressLoggerFactory() {
+        return new DefaultProgressLoggerFactory(get(ListenerManager.class).getBroadcaster(ProgressListener.class), new TrueTimeProvider());
+    }
+
+    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/core/src/main/groovy/org/gradle/testfixtures/internal/InMemoryCacheFactory.java b/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/InMemoryCacheFactory.java
new file mode 100644
index 0000000..fc455f5
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/InMemoryCacheFactory.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.testfixtures.internal;
+
+import org.gradle.CacheUsage;
+import org.gradle.api.internal.changedetection.InMemoryIndexedCache;
+import org.gradle.cache.*;
+
+import java.io.File;
+import java.util.Map;
+
+public class InMemoryCacheFactory implements CacheFactory {
+    public void close(PersistentCache cache) {
+    }
+
+    public PersistentCache open(final File cacheDir, CacheUsage usage, Map<String, ?> properties) {
+        cacheDir.mkdirs();
+        return new PersistentCache() {
+            public File getBaseDir() {
+                return cacheDir;
+            }
+
+            public boolean isValid() {
+                return false;
+            }
+
+            public void markValid() {
+            }
+
+            public <K, V> PersistentIndexedCache<K, V> openIndexedCache(Serializer<V> serializer) {
+                return new InMemoryIndexedCache<K, V>();
+            }
+
+            public <K, V> PersistentIndexedCache<K, V> openIndexedCache() {
+                return new InMemoryIndexedCache<K, V>();
+            }
+
+            public <T> PersistentStateCache<T> openStateCache() {
+                return new SimpleStateCache<T>(this, new DefaultSerializer<T>());
+            }
+        };
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/NoOpLoggingManager.java b/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/NoOpLoggingManager.java
new file mode 100644
index 0000000..e26982d
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/NoOpLoggingManager.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.testfixtures.internal;
+
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.logging.LoggingManager;
+import org.gradle.api.logging.StandardOutputListener;
+import org.gradle.logging.LoggingManagerInternal;
+import org.gradle.logging.internal.OutputEventListener;
+
+public class NoOpLoggingManager implements LoggingManagerInternal {
+    private LogLevel level = LogLevel.LIFECYCLE;
+    private LogLevel stdoutLevel = LogLevel.LIFECYCLE;
+
+    public LoggingManagerInternal captureStandardOutput(LogLevel level) {
+        stdoutLevel = level;
+        return this;
+    }
+
+    public LoggingManager disableStandardOutputCapture() {
+        stdoutLevel = null;
+        return this;
+    }
+
+    public boolean isStandardOutputCaptureEnabled() {
+        return stdoutLevel != null;
+    }
+
+    public LogLevel getStandardOutputCaptureLevel() {
+        return stdoutLevel;
+    }
+
+    public LoggingManagerInternal captureStandardError(LogLevel level) {
+        return this;
+    }
+
+    public LogLevel getLevel() {
+        return level;
+    }
+
+    public LoggingManagerInternal setLevel(LogLevel level) {
+        this.level = level;
+        return this;
+    }
+
+    public LogLevel getStandardErrorCaptureLevel() {
+        return LogLevel.ERROR;
+    }
+
+    public LoggingManagerInternal start() {
+        return this;
+    }
+
+    public LoggingManagerInternal stop() {
+        return this;
+    }
+
+    public void addStandardErrorListener(StandardOutputListener listener) {
+    }
+
+    public void addStandardOutputListener(StandardOutputListener listener) {
+    }
+
+    public void removeStandardOutputListener(StandardOutputListener listener) {
+    }
+
+    public void removeStandardErrorListener(StandardOutputListener listener) {
+    }
+
+    public void addOutputEventListener(OutputEventListener listener) {
+    }
+
+    public void removeOutputEventListener(OutputEventListener listener) {
+    }
+
+    public void colorStdOutAndStdErr(boolean colorOutput) {
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/TestTopLevelBuildServiceRegistry.java b/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/TestTopLevelBuildServiceRegistry.java
new file mode 100644
index 0000000..a896d8c
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/TestTopLevelBuildServiceRegistry.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.testfixtures.internal;
+
+import org.gradle.StartParameter;
+import org.gradle.api.internal.GradleDistributionLocator;
+import org.gradle.api.internal.project.ServiceRegistry;
+import org.gradle.api.internal.project.TopLevelBuildServiceRegistry;
+import org.gradle.configuration.GradleLauncherMetaData;
+import org.gradle.initialization.BuildClientMetaData;
+
+import java.io.File;
+
+public class TestTopLevelBuildServiceRegistry extends TopLevelBuildServiceRegistry {
+    private final File homeDir;
+
+    public TestTopLevelBuildServiceRegistry(ServiceRegistry parent, StartParameter startParameter, File homeDir) {
+        super(parent, startParameter);
+        this.homeDir = homeDir;
+    }
+
+    protected BuildClientMetaData createClientMetaData() {
+        return new GradleLauncherMetaData();
+    }
+
+    protected GradleDistributionLocator createGradleDistributionLocator() {
+        return new GradleDistributionLocator() {
+            public File getGradleHome() {
+                return homeDir;
+            }
+        };
+    }
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/ClasspathUtil.java b/subprojects/core/src/main/groovy/org/gradle/util/ClasspathUtil.java
index 89c1c71..1fddb43 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/ClasspathUtil.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/ClasspathUtil.java
@@ -35,15 +35,14 @@ public class ClasspathUtil {
                 method.invoke(classLoader, classpathElement);
             }
         } catch (Throwable t) {
-            t.printStackTrace();
-            throw new RuntimeException("Error, could not add URL to system classloader", t);
+            throw new RuntimeException("Error, could not add URL to classloader", t);
         }
     }
 
     public static List<URL> getClasspath(ClassLoader classLoader) {
         List<URL> implementationClassPath = new ArrayList<URL>();
-        for (
-                ClassLoader cl = classLoader; cl != ClassLoader.getSystemClassLoader().getParent(); cl = cl.getParent()) {
+        ClassLoader stopAt = ClassLoader.getSystemClassLoader() == null ? null : ClassLoader.getSystemClassLoader().getParent();
+        for (ClassLoader cl = classLoader; cl != null && cl != stopAt; cl = cl.getParent()) {
             if (cl instanceof URLClassLoader) {
                 implementationClassPath.addAll(Arrays.asList(((URLClassLoader) cl).getURLs()));
             }
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/DistributionLocator.java b/subprojects/core/src/main/groovy/org/gradle/util/DistributionLocator.java
new file mode 100644
index 0000000..67f6344
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/util/DistributionLocator.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+public class DistributionLocator {
+    private static final String ARTIFACTORY_RELEASE_REPOSITORY = "http://repo.gradle.org/gradle/distributions";
+    private static final String ARTIFACTORY_SNAPSHOT_REPOSITORY = "http://repo.gradle.org/gradle/distributions/gradle-snapshots";
+    private static final String CODEHAUS_RELEASE_REPOSITORY = "http://dist.codehaus.org/gradle";
+    private static final String CODEHAUS_SNAPSHOT_REPOSITORY = "http://snapshots.dist.codehaus.org/gradle";
+
+    public String getDistributionFor(GradleVersion version) {
+        return getDistribution(getDistributionRepository(version), version, "gradle", "bin");
+    }
+
+    public String getDistributionRepository(GradleVersion version) {
+        if (version.compareTo(GradleVersion.version("0.9")) >= 0) {
+            if (version.isSnapshot()) {
+                return ARTIFACTORY_SNAPSHOT_REPOSITORY;
+            }
+            return ARTIFACTORY_RELEASE_REPOSITORY;
+        } else {
+            if (version.isSnapshot()) {
+                return CODEHAUS_SNAPSHOT_REPOSITORY;
+            }
+            return CODEHAUS_RELEASE_REPOSITORY;
+        }
+    }
+
+    public String getDistribution(String repositoryUrl, GradleVersion version, String archiveName,
+                                  String archiveClassifier) {
+        return String.format("%s/%s-%s-%s.zip", repositoryUrl, archiveName, version.getVersion(), archiveClassifier);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/GUtil.java b/subprojects/core/src/main/groovy/org/gradle/util/GUtil.java
index 6f993cf..cbf3ba3 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/GUtil.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/GUtil.java
@@ -149,11 +149,9 @@ public class GUtil {
         return map;
     }
 
-    public static void addToMap(Map<String, String> dest, Properties src) {
-        Enumeration<?> enumeration = src.propertyNames();
-        while (enumeration.hasMoreElements()) {
-            Object o = enumeration.nextElement();
-            dest.put(o.toString(), src.getProperty(o.toString()));
+    public static void addToMap(Map<String, String> dest, Map<?, ?> src) {
+        for (Map.Entry<?, ?> entry : src.entrySet()) {
+            dest.put(entry.getKey().toString(), entry.getValue().toString());
         }
     }
 
@@ -211,10 +209,10 @@ public class GUtil {
         return map;
     }
 
-    public static String toString(Iterable<String> names) {
+    public static String toString(Iterable<?> names) {
         Formatter formatter = new Formatter();
         boolean first = true;
-        for (String name : names) {
+        for (Object name : names) {
             if (first) {
                 formatter.format("'%s'", name);
                 first = false;
@@ -226,8 +224,7 @@ public class GUtil {
     }
 
     /**
-     * Converts an arbitrary string to a camel-case string which can be used in a Java identifier. Eg, with_underscores
-     * -> withUnderscored
+     * Converts an arbitrary string to a camel-case string which can be used in a Java identifier. Eg, with_underscores -> withUnderscores
      */
     public static String toCamelCase(CharSequence string) {
         if (string == null) {
@@ -245,33 +242,54 @@ public class GUtil {
     }
 
     /**
-     * Converts an arbitrary string to space-separated words. Eg, camelCase -> camel case, with_underscores -> with
-     * underscores
+     * Converts an arbitrary string to upper case identifier with words separated by _. Eg, camelCase -> CAMEL_CASE
+     */
+    public static String toConstant(CharSequence string) {
+        if (string == null) {
+            return null;
+        }
+        return toWords(string, '_').toUpperCase();
+    }
+
+    /**
+     * Converts an arbitrary string to space-separated words. Eg, camelCase -> camel case, with_underscores -> with underscores
      */
     public static String toWords(CharSequence string) {
+        return toWords(string, ' ');
+    }
+
+    private static String toWords(CharSequence string, char separator) {
         if (string == null) {
             return null;
         }
         StringBuilder builder = new StringBuilder();
         int pos = 0;
-        boolean inSeparator = false;
-        for (; pos < string.length(); pos++) {
-            char ch = string.charAt(pos);
-            if (Character.isLowerCase(ch)) {
-                if (inSeparator && builder.length() > 0) {
-                    builder.append(' ');
-                }
-                builder.append(ch);
-                inSeparator = false;
-            } else if (Character.isUpperCase(ch)) {
-                if (builder.length() > 0) {
-                    builder.append(' ');
-                }
-                builder.append(Character.toLowerCase(ch));
-                inSeparator = false;
+        Matcher matcher = Pattern.compile("(\\p{Upper}*)(\\p{Lower}*)").matcher(string);
+        while (pos < string.length()) {
+            matcher.find(pos);
+            if (matcher.end() == pos) {
+                // Not looking at a match
+                pos++;
+                continue;
+            }
+            if (builder.length() > 0) {
+                builder.append(separator);
+            }
+            String group1 = matcher.group(1).toLowerCase();
+            String group2 = matcher.group(2);
+            if (group2.length() == 0) {
+                builder.append(group1);
             } else {
-                inSeparator = true;
+                if (group1.length() > 1) {
+                    builder.append(group1.substring(0, group1.length() - 1));
+                    builder.append(separator);
+                    builder.append(group1.substring(group1.length() - 1));
+                } else {
+                    builder.append(group1);
+                }
+                builder.append(group2);
             }
+            pos = matcher.end();
         }
 
         return builder.toString();
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/GradleVersion.java b/subprojects/core/src/main/groovy/org/gradle/util/GradleVersion.java
index 2a4b2fe..868da52 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/GradleVersion.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/GradleVersion.java
@@ -36,7 +36,7 @@ public class GradleVersion implements Comparable<GradleVersion> {
     private final static String VERSION = "version";
     private final static String FILE_NAME = "/org/gradle/version.properties";
     public final static String URL = "http://www.gradle.org";
-    private final static Pattern VERSION_PATTERN = Pattern.compile("(\\d+(\\.\\d+)+)(-(\\p{Alpha}+)-(\\d+))?(-(\\d{14}[-+]\\d{4}))?");
+    private final static Pattern VERSION_PATTERN = Pattern.compile("(\\d+(\\.\\d+)+)(-(\\p{Alpha}+)-(\\d+[a-z]?))?(-(\\d{14}[-+]\\d{4}))?");
 
     private final String version;
     private final String buildTime;
@@ -44,12 +44,12 @@ public class GradleVersion implements Comparable<GradleVersion> {
     private final String versionPart;
     private final Stage stage;
 
-    public GradleVersion() {
-        this(GUtil.loadProperties(GradleVersion.class.getResourceAsStream(FILE_NAME)));
+    public static GradleVersion current() {
+        return new GradleVersion(GUtil.loadProperties(GradleVersion.class.getResourceAsStream(FILE_NAME)));
     }
 
-    public GradleVersion(String version) {
-        this(properties(version));
+    public static GradleVersion version(String version) {
+        return new GradleVersion(properties(version));
     }
 
     private static Properties properties(String version) {
@@ -76,7 +76,8 @@ public class GradleVersion implements Comparable<GradleVersion> {
             } else if (matcher.group(4).equals("rc")) {
                 stageNumber = 3;
             }
-            stage = new Stage(stageNumber, Integer.parseInt(matcher.group(5)));
+            String stageString = matcher.group(5);
+            stage = new Stage(stageNumber, stageString);
         } else {
             stage = null;
         }
@@ -192,13 +193,26 @@ public class GradleVersion implements Comparable<GradleVersion> {
         return sb.toString();
     }
 
-    private static final class Stage implements Comparable<Stage> {
+    static final class Stage implements Comparable<Stage> {
         final int stage;
         final int number;
+        final Character patchNo;
 
-        private Stage(int stage, int number) {
+        Stage(int stage, String number) {
             this.stage = stage;
-            this.number = number;
+            Matcher m = Pattern.compile("(\\d+)([a-z])?").matcher(number);
+            try {
+                m.matches();
+                this.number = Integer.parseInt(m.group(1));
+            } catch (Exception e) {
+                throw new RuntimeException("Invalid stage small number: " + number, e);
+            }
+
+            if (m.groupCount() == 2 && m.group(2) != null) {
+                this.patchNo = m.group(2).charAt(0);
+            } else {
+                this.patchNo = '_';
+            }
         }
 
         public int compareTo(Stage other) {
@@ -214,6 +228,12 @@ public class GradleVersion implements Comparable<GradleVersion> {
             if (number < other.number) {
                 return -1;
             }
+            if (patchNo > other.patchNo) {
+                return 1;
+            }
+            if (patchNo < other.patchNo) {
+                return -1;
+            }
             return 0;
         }
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/Jvm.java b/subprojects/core/src/main/groovy/org/gradle/util/Jvm.java
index 0ec9bb6..1932538 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/Jvm.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/Jvm.java
@@ -50,6 +50,10 @@ public class Jvm {
         return new File(JavaEnvUtils.getJdkExecutable("javadoc"));
     }
 
+    public File getJpsExecutable() {
+        return new File(JavaEnvUtils.getJdkExecutable("jps"));
+    }
+
     public boolean isJava5Compatible() {
         return System.getProperty("java.version").startsWith("1.5") || isJava6Compatible();
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/LineBufferingOutputStream.java b/subprojects/core/src/main/groovy/org/gradle/util/LineBufferingOutputStream.java
index 5bf8a16..d541d87 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/LineBufferingOutputStream.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/LineBufferingOutputStream.java
@@ -50,7 +50,7 @@ public class LineBufferingOutputStream extends OutputStream {
         bufferIncrement = bufferLength;
         buf = new byte[bufferLength];
         count = 0;
-        lineSeparator = System.getProperty("line.separator").getBytes();
+        lineSeparator = SystemProperties.getLineSeparator().getBytes();
     }
 
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/NameMatcher.java b/subprojects/core/src/main/groovy/org/gradle/util/NameMatcher.java
index 57af8b3..1f8ac14 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/NameMatcher.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/NameMatcher.java
@@ -65,16 +65,20 @@ public class NameMatcher {
         Pattern normalisedCamelCasePattern = Pattern.compile(camelCasePattern.pattern(), Pattern.CASE_INSENSITIVE);
         String normalisedPattern = pattern.toUpperCase();
 
-        Set<String> matches1 = new TreeSet<String>();
-        Set<String> matches2 = new TreeSet<String>();
+        Set<String> caseInsensitiveMatches = new TreeSet<String>();
+        Set<String> caseSensitiveCamelCaseMatches = new TreeSet<String>();
+        Set<String> caseInsensitiveCamelCaseMatches = new TreeSet<String>();
 
         for (String candidate : items) {
+            if (candidate.equalsIgnoreCase(pattern)) {
+                caseInsensitiveMatches.add(candidate);
+            }
             if (camelCasePattern.matcher(candidate).matches()) {
-                matches1.add(candidate);
+                caseSensitiveCamelCaseMatches.add(candidate);
                 continue;
             }
             if (normalisedCamelCasePattern.matcher(candidate).lookingAt()) {
-                matches2.add(candidate);
+                caseInsensitiveCamelCaseMatches.add(candidate);
                 continue;
             }
             if (StringUtils.getLevenshteinDistance(normalisedPattern, candidate.toUpperCase()) <= Math.min(3, pattern.length() / 2)) {
@@ -82,10 +86,12 @@ public class NameMatcher {
             }
         }
 
-        if (!matches1.isEmpty()) {
-            matches.addAll(matches1);
+        if (!caseInsensitiveMatches.isEmpty()) {
+            matches.addAll(caseInsensitiveMatches);
+        } else if (!caseSensitiveCamelCaseMatches.isEmpty()) {
+            matches.addAll(caseSensitiveCamelCaseMatches);
         } else {
-            matches.addAll(matches2);
+            matches.addAll(caseInsensitiveCamelCaseMatches);
         }
 
         if (matches.size() == 1) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/OperatingSystem.java b/subprojects/core/src/main/groovy/org/gradle/util/OperatingSystem.java
index 5adfb59..948af91 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/OperatingSystem.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/OperatingSystem.java
@@ -34,6 +34,15 @@ public class OperatingSystem {
             }
             return scriptPath + ".bat";
         }
+
+        @Override
+        public String getNativePrefix() {
+            String arch = System.getProperty("os.arch");
+            if ("i386".equals(arch)) {
+                arch = "x86";
+            }
+            return  "win32-" + arch;
+        }
     };
 
     private static final OperatingSystem OS_X = new OperatingSystem() {
@@ -41,6 +50,11 @@ public class OperatingSystem {
         public boolean isCaseSensitiveFileSystem() {
             return false;
         }
+
+        @Override
+        public String getNativePrefix() {
+            return "darwin";
+        }
     };
 
     private static final OperatingSystem OTHER = new OperatingSystem();
@@ -82,4 +96,25 @@ public class OperatingSystem {
     public String getScriptName(String scriptPath) {
         return scriptPath;
     }
+
+    public String getNativePrefix() {
+        String name = System.getProperty("os.name");
+        String arch = System.getProperty("os.arch");
+        String osPrefix = name.toLowerCase();
+        if ("x86".equals(arch)) {
+            arch = "i386";
+        }
+        if ("x86_64".equals(arch)) {
+            arch = "amd64";
+        }
+        if ("powerpc".equals(arch)) {
+            arch = "ppc";
+        }
+        int space = osPrefix.indexOf(" ");
+        if (space != -1) {
+            osPrefix = osPrefix.substring(0, space);
+        }
+        osPrefix += "-" + arch;
+        return osPrefix;
+    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/SystemProperties.java b/subprojects/core/src/main/groovy/org/gradle/util/SystemProperties.java
new file mode 100644
index 0000000..a7155d7
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/util/SystemProperties.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+/**
+ * Provides access to frequently used system properties.
+ */
+public class SystemProperties {
+    public static String getLineSeparator() {
+        return System.getProperty("line.separator");
+    }
+
+    public static String getJavaIoTmpDir() {
+        return System.getProperty("java.io.tmpdir");
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/TextUtil.java b/subprojects/core/src/main/groovy/org/gradle/util/TextUtil.java
index fc06fe0..493f064 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/TextUtil.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/TextUtil.java
@@ -18,15 +18,37 @@ package org.gradle.util;
 
 public class TextUtil {
     /**
-     * The native line separator for the current platform (e.g. \n or \r\n).
+     * Returns the line separator for Windows.
      */
-    public static final String LINE_SEPARATOR = System.getProperty("line.separator");
+    public static String getWindowsLineSeparator() {
+        return "\r\n";
+    }
+
+    /**
+     * Returns the line separator for Unix.
+     */
+    public static String getUnixLineSeparator() {
+        return "\n";
+    }
+
+    /**
+     * Returns the line separator for this platform.
+     */
+    public static String getPlatformLineSeparator() {
+        return SystemProperties.getLineSeparator();
+    }
+
+    /**
+     * Converts all line separators in the specified string to the specified line separator.
+     */
+    public static String convertLineSeparators(String str, String sep) {
+        return str.replaceAll("\r\n|\r|\n", sep);
+    }
 
     /**
-     * Replaces all line separators (\r, \n, \r\n) in the specified string with
-     * the platform's native line separator.
+     * Converts all line separators in the specified string to the the platform's line separator.
      */
-    public static String toNativeLineSeparators(String str) {
-        return str.replaceAll("\r\n|\r|\n", LINE_SEPARATOR);
+    public static String toPlatformLineSeparators(String str) {
+        return convertLineSeparators(str, getPlatformLineSeparator());
     }
 }
diff --git a/subprojects/core/src/main/resources/org/gradle/configuration/default-imports.txt b/subprojects/core/src/main/resources/org/gradle/configuration/default-imports.txt
index eb266ee..af4bd51 100644
--- a/subprojects/core/src/main/resources/org/gradle/configuration/default-imports.txt
+++ b/subprojects/core/src/main/resources/org/gradle/configuration/default-imports.txt
@@ -12,6 +12,8 @@ import org.gradle.api.invocation.*
 import org.gradle.api.java.archives.*
 import org.gradle.api.logging.*
 import org.gradle.api.plugins.*
+import org.gradle.plugins.ide.eclipse.*
+import org.gradle.plugins.ide.idea.*
 import org.gradle.api.plugins.quality.*
 import org.gradle.api.specs.*
 import org.gradle.api.tasks.*
diff --git a/subprojects/core/src/test/groovy/org/gradle/BuildExceptionReporterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/BuildExceptionReporterTest.groovy
index bc89d49..64c2ce3 100644
--- a/subprojects/core/src/test/groovy/org/gradle/BuildExceptionReporterTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/BuildExceptionReporterTest.groovy
@@ -24,6 +24,7 @@ import org.gradle.initialization.BuildClientMetaData
 import org.gradle.logging.StyledTextOutputFactory
 import org.gradle.logging.internal.TestStyledTextOutput
 import spock.lang.Specification
+import org.gradle.groovy.scripts.ScriptSource
 
 class BuildExceptionReporterTest extends Specification {
     final TestStyledTextOutput output = new TestStyledTextOutput()
@@ -55,7 +56,7 @@ class BuildExceptionReporterTest extends Specification {
 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.
+Run with {userinput}--debug{normal} option to get additional debug info.
 
 * Exception is:
 java.lang.RuntimeException: <message>
@@ -75,7 +76,7 @@ java.lang.RuntimeException: <message>
 <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.
+Run with {userinput}--stacktrace{normal} option to get the stack trace. Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
 '''
     }
 
@@ -91,7 +92,7 @@ Run with {userinput}-s{normal} or {userinput}-d{normal} option to get more detai
 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.
+Run with {userinput}--stacktrace{normal} option to get the stack trace. Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
 '''
     }
 
@@ -111,7 +112,7 @@ Run with {userinput}-s{normal} or {userinput}-d{normal} option to get more detai
 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.
+Run with {userinput}--stacktrace{normal} option to get the stack trace. Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
 '''
     }
 
@@ -132,7 +133,7 @@ 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.
+Run with {userinput}--stacktrace{normal} option to get the stack trace. Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
 '''
     }
 
@@ -152,7 +153,33 @@ Run with {userinput}-s{normal} or {userinput}-d{normal} option to get more detai
 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.
+Run with {userinput}--stacktrace{normal} option to get the stack trace. Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
+'''
+    }
+
+    def showsStacktraceOfCauseOfLocationAwareException() {
+        startParameter.showStacktrace = ShowStacktrace.ALWAYS
+
+        Throwable exception = exception("<location>", "<message>", new GradleException('<failure>'))
+
+        expect:
+        reporter.buildFinished(result(exception))
+        output.value == '''
+{failure}FAILURE: {normal}{failure}Build failed with an exception.{normal}
+
+* Where:
+<location>
+
+* What went wrong:
+<message>
+Cause: <failure>
+
+* Try:
+Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
+
+* Exception is:
+org.gradle.api.GradleException: <failure>
+{stacktrace}
 '''
     }
 
@@ -186,7 +213,7 @@ Run {userinput}[gradle tasks]{normal} to get a list of available tasks.
 <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.
+Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
 
 * Exception is:
 org.gradle.api.GradleException: <message>
@@ -208,7 +235,7 @@ org.gradle.api.GradleException: <message>
 <message>
 
 * Try:
-Run with {userinput}-d{normal} option to get more details. 
+Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
 
 * Exception is:
 org.gradle.api.GradleException: <message>
@@ -246,9 +273,13 @@ org.gradle.api.GradleException: <message>
         _ * exception.location >> location
         _ * exception.originalMessage >> message
         _ * exception.reportableCauses >> (causes as List)
+        _ * exception.cause >> causes[0]
         exception
     }
 }
 
-public abstract class TestException extends GradleException implements LocationAwareException {
+public abstract class TestException extends LocationAwareException {
+    TestException(Throwable cause, ScriptSource source, Integer lineNumber) {
+        super(cause, source, lineNumber)
+    }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/TaskExecutionLoggerTest.java b/subprojects/core/src/test/groovy/org/gradle/TaskExecutionLoggerTest.java
index 110eb72..733cd74 100644
--- a/subprojects/core/src/test/groovy/org/gradle/TaskExecutionLoggerTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/TaskExecutionLoggerTest.java
@@ -57,9 +57,12 @@ public class TaskExecutionLoggerTest {
         context.checking(new Expectations() {{
             allowing(gradle).getParent();
             will(returnValue(null));
-            one(progressLoggerFactory).start(TaskExecutionLogger.class.getName(), ":path");
+            one(progressLoggerFactory).newOperation(TaskExecutionLogger.class);
             will(returnValue(progressLogger));
-            one(progressLogger).progress(":path");
+            one(progressLogger).setDescription("Execute :path");
+            one(progressLogger).setShortDescription(":path");
+            one(progressLogger).setLoggingHeader(":path");
+            one(progressLogger).started();
         }});
 
         executionLogger.beforeExecute(task);
@@ -67,7 +70,7 @@ public class TaskExecutionLoggerTest {
         context.checking(new Expectations() {{
             allowing(state).getSkipMessage();
             will(returnValue(null));
-            one(progressLogger).completed();
+            one(progressLogger).completed(null);
         }});
 
         executionLogger.afterExecute(task, state);
@@ -85,9 +88,12 @@ public class TaskExecutionLoggerTest {
             allowing(rootProject).getName();
             will(returnValue("build"));
 
-            one(progressLoggerFactory).start(TaskExecutionLogger.class.getName(), ":build:path");
+            one(progressLoggerFactory).newOperation(TaskExecutionLogger.class);
             will(returnValue(progressLogger));
-            one(progressLogger).progress(":build:path");
+            one(progressLogger).setDescription("Execute :build:path");
+            one(progressLogger).setShortDescription(":build:path");
+            one(progressLogger).setLoggingHeader(":build:path");
+            one(progressLogger).started();
         }});
 
         executionLogger.beforeExecute(task);
@@ -95,7 +101,7 @@ public class TaskExecutionLoggerTest {
         context.checking(new Expectations() {{
             allowing(state).getSkipMessage();
             will(returnValue(null));
-            one(progressLogger).completed();
+            one(progressLogger).completed(null);
         }});
 
         executionLogger.afterExecute(task, state);
@@ -106,19 +112,17 @@ public class TaskExecutionLoggerTest {
         context.checking(new Expectations() {{
             allowing(gradle).getParent();
             will(returnValue(null));
-            one(progressLoggerFactory).start(TaskExecutionLogger.class.getName(), ":path");
-            will(returnValue(progressLogger));
-            one(progressLogger).progress(":path");
-        }});
-
-        executionLogger.beforeExecute(task);
 
-        context.checking(new Expectations() {{
+            one(progressLoggerFactory).newOperation(TaskExecutionLogger.class);
+            will(returnValue(progressLogger));
             allowing(state).getSkipMessage();
             will(returnValue("skipped"));
             one(progressLogger).completed("skipped");
+
+            ignoring(progressLogger);
         }});
 
+        executionLogger.beforeExecute(task);
         executionLogger.afterExecute(task, state);
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/GeneratorTaskTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/GeneratorTaskTest.groovy
deleted file mode 100644
index 8a748e6..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/GeneratorTaskTest.groovy
+++ /dev/null
@@ -1,110 +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
-
-import org.gradle.api.tasks.GeneratorTask
-import org.gradle.util.HelperUtil
-import org.gradle.util.TemporaryFolder
-import org.junit.Rule
-import spock.lang.Specification
-import org.gradle.api.internal.tasks.generator.Generator
-
-class GeneratorTaskTest extends Specification {
-    @Rule public final TemporaryFolder tmpDir = new TemporaryFolder()
-    final Generator<TestConfigurationObject> generator = Mock()
-    final File inputFile = tmpDir.file('input')
-    final File outputFile = tmpDir.file('output')
-    final GeneratorTask<TestConfigurationObject> task = HelperUtil.createTask(GeneratorTask)
-
-    def setup() {
-        task.inputFile = inputFile
-        task.outputFile = outputFile
-        task.generator = generator
-    }
-
-    def usesOutputFileAsDefaultInputFile() {
-        when:
-        task.inputFile = null
-
-        then:
-        task.inputFile == task.outputFile
-
-        when:
-        task.inputFile = inputFile
-
-        then:
-        task.inputFile == inputFile
-    }
-    
-    def mergesConfigurationWhenInputFileExists() {
-        def configObject = new TestConfigurationObject()
-        inputFile.text = 'config'
-
-        when:
-        task.generate()
-
-        then:
-        1 * generator.read(inputFile) >> configObject
-        1 * generator.configure(configObject)
-        1 * generator.write(configObject, outputFile)
-        0 * _._
-    }
-
-    def generatesConfigurationWhenInputFileDoesNotExist() {
-        def configObject = new TestConfigurationObject()
-
-        when:
-        task.generate()
-
-        then:
-        1 * generator.defaultInstance() >> configObject
-        1 * generator.configure(configObject)
-        1 * generator.write(configObject, outputFile)
-        0 * _._
-    }
-
-    def executesActionBeforeConfiguringObject() {
-        def configObject = new TestConfigurationObject()
-        Action<TestConfigurationObject> action = Mock()
-        task.beforeConfigured(action)
-
-        when:
-        task.generate()
-
-        then:
-        1 * generator.defaultInstance() >> configObject
-        1 * action.execute(configObject)
-        1 * generator.configure(configObject)
-    }
-
-    def executesActionAfterConfiguringObject() {
-        def configObject = new TestConfigurationObject()
-        Action<TestConfigurationObject> action = Mock()
-        task.whenConfigured(action)
-
-        when:
-        task.generate()
-
-        then:
-        1 * generator.defaultInstance() >> configObject
-        1 * generator.configure(configObject)
-        1 * action.execute(configObject)
-    }
-}
-
-class TestConfigurationObject {
-
-}
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/JavaVersionTest.java b/subprojects/core/src/test/groovy/org/gradle/api/JavaVersionTest.java
index 0a27d97..9c5a288 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/JavaVersionTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/JavaVersionTest.java
@@ -26,6 +26,7 @@ public class JavaVersionTest {
         assertThat(JavaVersion.VERSION_1_4.toString(), equalTo("1.4"));
         assertThat(JavaVersion.VERSION_1_5.toString(), equalTo("1.5"));
         assertThat(JavaVersion.VERSION_1_6.toString(), equalTo("1.6"));
+        assertThat(JavaVersion.VERSION_1_7.toString(), equalTo("1.7"));
     }
 
     @Test
@@ -38,6 +39,7 @@ public class JavaVersionTest {
 
         assertThat(JavaVersion.toVersion("5"), equalTo(JavaVersion.VERSION_1_5));
         assertThat(JavaVersion.toVersion("6"), equalTo(JavaVersion.VERSION_1_6));
+        assertThat(JavaVersion.toVersion("7"), equalTo(JavaVersion.VERSION_1_7));
     }
 
     @Test
@@ -45,7 +47,7 @@ public class JavaVersionTest {
         conversionFails("1");
         conversionFails("2");
 
-        conversionFails("7");
+        conversionFails("8");
 
         conversionFails("a");
         conversionFails("");
@@ -68,13 +70,15 @@ public class JavaVersionTest {
         assertThat(JavaVersion.toVersion(1.5), equalTo(JavaVersion.VERSION_1_5));
         assertThat(JavaVersion.toVersion(5), equalTo(JavaVersion.VERSION_1_5));
         assertThat(JavaVersion.toVersion(6), equalTo(JavaVersion.VERSION_1_6));
+        assertThat(JavaVersion.toVersion(7), equalTo(JavaVersion.VERSION_1_7));
+        assertThat(JavaVersion.toVersion(1.7), equalTo(JavaVersion.VERSION_1_7));
     }
     
     @Test
     public void failsToConvertNumberToVersionForUnknownVersion() {
         conversionFails(1);
         conversionFails(2);
-        conversionFails(7);
+        conversionFails(8);
         conversionFails(1.21);
         conversionFails(2.0);
         conversionFails(4.2);
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/artifacts/specs/DependencySpecsTest.java b/subprojects/core/src/test/groovy/org/gradle/api/artifacts/specs/DependencySpecsTest.java
deleted file mode 100644
index 5f72467..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/artifacts/specs/DependencySpecsTest.java
+++ /dev/null
@@ -1,43 +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 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/core/src/test/groovy/org/gradle/api/artifacts/specs/TypeTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/artifacts/specs/TypeTest.groovy
new file mode 100644
index 0000000..d430320
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/artifacts/specs/TypeTest.groovy
@@ -0,0 +1,38 @@
+/*
+ * 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.ProjectDependency
+import org.gradle.api.artifacts.ExternalDependency
+import org.gradle.api.artifacts.Dependency
+
+import spock.lang.Specification
+
+class TypeTest extends Specification {
+    def "EXTERNAL matches external dependencies"() {
+        expect:
+        Type.EXTERNAL.isSatisfiedBy(Mock(ExternalDependency))
+        !Type.EXTERNAL.isSatisfiedBy(Mock(ProjectDependency))
+        !Type.EXTERNAL.isSatisfiedBy(Mock(Dependency))
+    }
+
+    def "PROJECT matches project dependencies"() {
+        expect:
+        Type.PROJECT.isSatisfiedBy(Mock(ProjectDependency))
+        !Type.PROJECT.isSatisfiedBy(Mock(ExternalDependency))
+        !Type.PROJECT.isSatisfiedBy(Mock(Dependency))
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/file/FileVisitorUtil.groovy b/subprojects/core/src/test/groovy/org/gradle/api/file/FileVisitorUtil.groovy
index 1b5d55f..9847032 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/file/FileVisitorUtil.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/file/FileVisitorUtil.groovy
@@ -17,8 +17,14 @@ package org.gradle.api.file
 
 import static org.junit.Assert.*
 import static org.hamcrest.Matchers.*
+import org.gradle.api.internal.file.collections.MinimalFileTree
+import org.gradle.api.internal.file.collections.FileTreeAdapter
 
 class FileVisitorUtil {
+    static def assertCanStopVisiting(MinimalFileTree tree) {
+        assertCanStopVisiting(new FileTreeAdapter(tree))
+    }
+
     static def assertCanStopVisiting(FileTree tree) {
         boolean found = false
         FileVisitor visitor = [
@@ -35,7 +41,11 @@ class FileVisitorUtil {
         tree.visit(visitor)
         assertTrue(found)
     }
-    
+
+    static def assertVisits(MinimalFileTree tree, Iterable<String> expectedFiles, Iterable<String> expectedDirs) {
+        assertVisits(new FileTreeAdapter(tree), expectedFiles, expectedDirs)
+    }
+
     static def assertVisits(FileTree tree, Iterable<String> expectedFiles, Iterable<String> expectedDirs) {
         Set files = [] as Set
         Set dirs = [] as Set
@@ -76,4 +86,18 @@ class FileVisitorUtil {
         assertThat(files, equalTo(expectedFiles + expectedDirs as Set))
     }
 
+    static def assertVisits(FileTree tree, Map<String, File> files) {
+        Map<String, File> visited = [:]
+        FileVisitor visitor = [
+                visitFile: {FileVisitDetails details ->
+                    visited.put(details.path, details.file)
+                },
+                visitDir: {FileVisitDetails details ->
+                }
+        ] as FileVisitor
+
+        tree.visit(visitor)
+
+        assertThat(visited, equalTo(files))
+    }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/AbstractMultiCauseExceptionTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/AbstractMultiCauseExceptionTest.groovy
new file mode 100644
index 0000000..633ee4f
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/AbstractMultiCauseExceptionTest.groovy
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+class AbstractMultiCauseExceptionTest extends Specification {
+    def getCauseReturnsTheFirstCause() {
+        def cause1 = new RuntimeException()
+        def cause2 = new RuntimeException()
+        def failure = new TestMultiCauseException('message', [cause1, cause2])
+
+        expect:
+        failure.cause == cause1
+        failure.causes == [cause1, cause2]
+    }
+
+    def getCauseReturnsNullWhenThereAreNoCauses() {
+        def failure = new TestMultiCauseException('message', [])
+
+        expect:
+        failure.cause == null
+        failure.causes == []
+    }
+    
+    def printStackTraceWithMultipleCauses() {
+        RuntimeException cause1 = new RuntimeException('cause1')
+        RuntimeException cause2 = new RuntimeException('cause2')
+        def failure = new TestMultiCauseException('message', [cause1, cause2])
+        def outstr = new StringWriter()
+
+        when:
+        outstr.withPrintWriter { writer ->
+            failure.printStackTrace(writer)
+        }
+
+        then:
+        outstr.toString().contains("${TestMultiCauseException.name}: message")
+        outstr.toString().contains("Cause 1: ${RuntimeException.name}: cause1")
+        outstr.toString().contains("Cause 2: ${RuntimeException.name}: cause2")
+    }
+    
+    def printStackTraceWithSingleCause() {
+        RuntimeException cause1 = new RuntimeException('cause1')
+        def failure = new TestMultiCauseException('message', [cause1])
+        def outstr = new StringWriter()
+
+        when:
+        outstr.withPrintWriter { writer ->
+            failure.printStackTrace(writer)
+        }
+
+        then:
+        outstr.toString().contains("${TestMultiCauseException.name}: message")
+        outstr.toString().contains("Caused by: ${RuntimeException.name}: cause1")
+    }
+}
+
+class TestMultiCauseException extends AbstractMultiCauseException {
+    TestMultiCauseException(String message, Iterable<? extends Throwable> causes) {
+        super(message, causes)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/XmlTransformerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/XmlTransformerTest.groovy
index 2a8df9f..88c0d0c 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/XmlTransformerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/XmlTransformerTest.groovy
@@ -198,7 +198,7 @@ class XmlTransformerTest extends Specification {
     }
 
     private void looksLike(String expected, String actual) {
-        assert actual == TextUtil.toNativeLineSeparators(addXmlDeclaration(expected))
+        assert actual == TextUtil.toPlatformLineSeparators(addXmlDeclaration(expected))
     }
 
     private String addXmlDeclaration(String value) {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/CachingDependencyResolveContextTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/CachingDependencyResolveContextTest.groovy
index a0db932..186972c 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/CachingDependencyResolveContextTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/CachingDependencyResolveContextTest.groovy
@@ -34,7 +34,7 @@ class CachingDependencyResolveContextTest extends Specification {
         then:
         1 * dependency.resolve(context) >> { context.add(fileCollection) }
         files instanceof UnionFileCollection
-        files.sourceCollections == [fileCollection]
+        files.sources as List == [fileCollection]
     }
 
     def resolvesADependencyInternal() {
@@ -50,7 +50,7 @@ class CachingDependencyResolveContextTest extends Specification {
         1 * dependency.resolve(context) >> { context.add(otherDependency) }
         1 * otherDependency.resolve(context) >> { context.add(fileCollection) }
         files instanceof UnionFileCollection
-        files.sourceCollections == [fileCollection]
+        files.sources as List == [fileCollection]
     }
 
     def failsToResolveAnyOtherType() {
@@ -79,6 +79,6 @@ class CachingDependencyResolveContextTest extends Specification {
         1 * dependency.resolve(context) >> { context.add(otherDependency) }
         1 * otherDependency.resolve(context) >> { context.add(fileCollection); context.add(dependency) }
         files instanceof UnionFileCollection
-        files.sourceCollections == [fileCollection]
+        files.sources as List == [fileCollection]
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolverContainerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolverContainerTest.groovy
index c26ffa6..583b93f 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolverContainerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolverContainerTest.groovy
@@ -27,21 +27,21 @@ import org.gradle.api.artifacts.UnknownRepositoryException
 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.internal.ClassGenerator
 import org.gradle.api.internal.artifacts.ivyservice.ResolverFactory
+import org.gradle.api.internal.file.FileResolver
 import org.gradle.util.JUnit4GroovyMockery
 import org.junit.Before
 import org.junit.Test
 import static org.hamcrest.Matchers.*
 import static org.junit.Assert.*
-import org.gradle.api.internal.file.FileResolver
-import org.gradle.api.internal.ClassGenerator
 
 /**
  * @author Hans Dockter
  */
 class DefaultResolverContainerTest {
     static final String TEST_REPO_NAME = 'reponame'
-    
+
     DefaultResolverContainer resolverContainer
 
     RepositoryCacheManager dummyCacheManager = new DefaultRepositoryCacheManager()
@@ -126,27 +126,27 @@ class DefaultResolverContainerTest {
         assertEquals([expectedResolver, expectedResolver3, expectedResolver2], resolverContainer.resolvers)
     }
 
-    @Test (expected = InvalidUserDataException) public void testAddWithNullUserDescription() {
+    @Test(expected = InvalidUserDataException) public void testAddWithNullUserDescription() {
         resolverContainer.add(null)
     }
 
-    @Test (expected = InvalidUserDataException) public void testAddFirstWithNullUserDescription() {
+    @Test(expected = InvalidUserDataException) public void testAddFirstWithNullUserDescription() {
         resolverContainer.addFirst(null)
     }
 
-    @Test (expected = InvalidUserDataException) public void testAddBeforeWithNullUserDescription() {
+    @Test(expected = InvalidUserDataException) public void testAddBeforeWithNullUserDescription() {
         resolverContainer.addBefore(null, expectedName)
     }
 
-    @Test (expected = InvalidUserDataException) public void testAddBeforeWithUnknownResolver() {
+    @Test(expected = InvalidUserDataException) public void testAddBeforeWithUnknownResolver() {
         resolverContainer.addBefore(expectedUserDescription2, 'unknownName')
     }
 
-    @Test (expected = InvalidUserDataException) public void testAddAfterWithNullUserDescription() {
+    @Test(expected = InvalidUserDataException) public void testAddAfterWithNullUserDescription() {
         resolverContainer.addAfter(null, expectedName)
     }
 
-    @Test (expected = InvalidUserDataException) public void testAddAfterWithUnknownResolver() {
+    @Test(expected = InvalidUserDataException) public void testAddAfterWithUnknownResolver() {
         resolverContainer.addBefore(expectedUserDescription2, 'unknownName')
     }
 
@@ -156,7 +156,7 @@ class DefaultResolverContainerTest {
         assertEquals([expectedResolver2, expectedResolver], resolverContainer.resolvers)
     }
 
-    @Test(expected =  InvalidUserDataException)
+    @Test(expected = InvalidUserDataException)
     public void testAddWithUnnamedResolver() {
         expectedResolver.name = null
         resolverContainer.add(expectedUserDescription).is(expectedResolver)
@@ -171,10 +171,9 @@ class DefaultResolverContainerTest {
             assertThat(e.message, equalTo("Repository with name 'unknown' not found."))
         }
     }
-    
+
     @Test
     public void createMavenUploader() {
-        // todo we have to specify the class name, as this class is extended. This is a Groovy bug. As soon as we switch to a new Groovy version, we can refactor this. 
         assertSame(prepareMavenDeployerTests(), resolverContainer.createMavenDeployer(DefaultResolverContainerTest.TEST_REPO_NAME));
     }
 
@@ -193,9 +192,9 @@ class DefaultResolverContainerTest {
 
     protected DependencyResolver prepareMavenResolverTests(Class resolverType, String createMethod) {
         File testPomDir = new File("pomdir");
-        ConfigurationContainer configurationContainer = [:] as ConfigurationContainer
-        Conf2ScopeMappingContainer conf2ScopeMappingContainer = [:] as Conf2ScopeMappingContainer
-        FileResolver fileResolver = [:] as FileResolver
+        ConfigurationContainer configurationContainer = context.mock(ConfigurationContainer.class)
+        Conf2ScopeMappingContainer conf2ScopeMappingContainer = context.mock(Conf2ScopeMappingContainer.class)
+        FileResolver fileResolver = context.mock(FileResolver.class)
         resolverContainer.setMavenPomDir(testPomDir)
         resolverContainer.setConfigurationContainer(configurationContainer)
         resolverContainer.setMavenScopeMappings(conf2ScopeMappingContainer)
@@ -210,7 +209,7 @@ class DefaultResolverContainerTest {
                     withParam(same(conf2ScopeMappingContainer)),
                     withParam(same(fileResolver)));
             will(returnValue(expectedResolver))
-            one(resolverFactoryMock).createResolver(expectedResolver);
+            allowing(resolverFactoryMock).createResolver(expectedResolver);
             will(returnValue(expectedResolver))
         }
         expectedResolver
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationTest.java
index fff5822..39817aa 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationTest.java
@@ -43,7 +43,6 @@ import java.io.File;
 import java.util.*;
 
 import static org.gradle.util.Matchers.isEmpty;
-import static org.gradle.util.Matchers.strictlyEqual;
 import static org.gradle.util.WrapUtil.*;
 import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.*;
@@ -345,15 +344,6 @@ public class DefaultConfigurationTest {
         assertThat(configuration.getUploadTaskName(), equalTo("uploadName"));
     }
 
-    @Test
-    public void equality() {
-        Configuration sameConf = createNamedConfiguration("path", "name");
-        Configuration differentPath = createNamedConfiguration("other", "name");
-
-        assertThat(configuration, strictlyEqual(sameConf));
-        assertThat(configuration, not(equalTo(differentPath)));
-    }
-
     private DefaultConfiguration createNamedConfiguration(String confName) {
         return new DefaultConfiguration(confName, confName, configurationContainer, ivyServiceStub);
     }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultProjectDependencyTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultProjectDependencyTest.java
index 72c69f7..5e28a14 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultProjectDependencyTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultProjectDependencyTest.java
@@ -134,19 +134,9 @@ public class DefaultProjectDependencyTest extends AbstractModuleDependencyTest {
     @Test
     public void resolveNotDelegatesToProjectDependenciesInTargetConfigurationIfConfigurationIsNonTransitive() {
         final DependencyResolveContext resolveContext = context.mock(DependencyResolveContext.class);
-        final Dependency projectSelfResolvingDependency = context.mock(Dependency.class);
-        final ProjectDependency transitiveProjectDependencyStub = context.mock(ProjectDependency.class);
         context.checking(new Expectations() {{
-            allowing(projectConfigurationsStub).getByName("conf1");
-            will(returnValue(projectConfigurationStub));
-
-            allowing(projectConfigurationStub).getAllDependencies();
-            will(returnValue(toSet(projectSelfResolvingDependency, transitiveProjectDependencyStub)));
-
             allowing(resolveContext).isTransitive();
             will(returnValue(false));
-
-            one(resolveContext).add(projectSelfResolvingDependency);
         }});
         DefaultProjectDependency projectDependency = new DefaultProjectDependency(dependencyProjectStub, "conf1",
                 instruction);
@@ -155,18 +145,7 @@ public class DefaultProjectDependencyTest extends AbstractModuleDependencyTest {
     
     @Test
     public void resolveNotDelegatesToTransitiveProjectDependenciesIfProjectDependencyIsNonTransitive() {
-        final DependencyResolveContext resolveContext = context.mock(DependencyResolveContext.class);
-        final SelfResolvingDependency projectSelfResolvingDependency = context.mock(SelfResolvingDependency.class);
-        final ProjectDependency transitiveProjectDependencyStub = context.mock(ProjectDependency.class);
-        context.checking(new Expectations() {{
-            allowing(projectConfigurationsStub).getByName("conf1");
-            will(returnValue(projectConfigurationStub));
-
-            allowing(projectConfigurationStub).getAllDependencies();
-            will(returnValue(toSet(projectSelfResolvingDependency, transitiveProjectDependencyStub)));
-
-            one(resolveContext).add(projectSelfResolvingDependency);
-        }});
+        DependencyResolveContext resolveContext = context.mock(DependencyResolveContext.class);
         DefaultProjectDependency projectDependency = new DefaultProjectDependency(dependencyProjectStub, "conf1", instruction);
         projectDependency.setTransitive(false);
         projectDependency.resolve(resolveContext);
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultRepositoryHandlerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultRepositoryHandlerTest.groovy
index 74e8b3e..8182c35 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultRepositoryHandlerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultRepositoryHandlerTest.groovy
@@ -17,7 +17,7 @@
 package org.gradle.api.internal.artifacts.dsl
 
 import org.apache.ivy.plugins.resolver.ResolverSettings
-import org.apache.maven.artifact.ant.RemoteRepository
+//import org.apache.maven.artifact.ant.RemoteRepository
 import org.gradle.api.InvalidUserDataException
 import org.gradle.api.artifacts.ResolverContainer
 import org.gradle.api.artifacts.dsl.RepositoryHandler
@@ -28,10 +28,18 @@ import org.gradle.util.HashUtil
 import org.junit.Test
 import static org.junit.Assert.*
 import org.gradle.api.internal.AsmBackedClassGenerator
+import org.gradle.api.artifacts.dsl.IvyArtifactRepository
+import org.gradle.api.Action
+import org.junit.runner.RunWith
+import org.jmock.integration.junit4.JMock
+import org.gradle.api.internal.artifacts.repositories.ArtifactRepositoryInternal
+import org.apache.ivy.plugins.resolver.DependencyResolver
+import org.hamcrest.Matchers
 
 /**
  * @author Hans Dockter
  */
+ at RunWith(JMock)
 class DefaultRepositoryHandlerTest extends DefaultResolverContainerTest {
     static final String TEST_REPO_URL = 'http://www.gradle.org'
 
@@ -68,7 +76,7 @@ class DefaultRepositoryHandlerTest extends DefaultResolverContainerTest {
         assertEquals([expectedResolver], repositoryHandler.getResolvers())
     }
 
-    @Test (expected = InvalidUserDataException)
+    @Test(expected = InvalidUserDataException)
     public void testFlatDirWithMissingDirs() {
         repositoryHandler.flatDir([name: 'someName'])
     }
@@ -157,7 +165,7 @@ class DefaultRepositoryHandlerTest extends DefaultResolverContainerTest {
 
     private def prepareFlatDirResolverCreation(String expectedName, File[] expectedDirs) {
         context.checking {
-          one(resolverFactoryMock).createFlatDirResolver(expectedName, expectedDirs); will(returnValue(expectedResolver))
+            one(resolverFactoryMock).createFlatDirResolver(expectedName, expectedDirs); will(returnValue(expectedResolver))
         }
     }
 
@@ -170,9 +178,9 @@ class DefaultRepositoryHandlerTest extends DefaultResolverContainerTest {
     }
 
     private def prepareResolverFactoryToTakeAndReturnExpectedResolver() {
-      context.checking {
-        one(resolverFactoryMock).createResolver(expectedResolver); will(returnValue(expectedResolver))
-      }
+        context.checking {
+            one(resolverFactoryMock).createResolver(expectedResolver); will(returnValue(expectedResolver))
+        }
     }
 
     @Test
@@ -192,34 +200,34 @@ class DefaultRepositoryHandlerTest extends DefaultResolverContainerTest {
         assertSame(expectedResolver, repositoryHandler.mavenDeployer(name: expectedName));
     }
 
-    @Test
-    public void mavenDeployerWithNameAndClosure() {
-        GroovyMavenDeployer expectedResolver = prepareMavenDeployerTests()
-        String expectedName = RepositoryHandler.DEFAULT_MAVEN_DEPLOYER_NAME + "-" +
-                System.identityHashCode(expectedResolver)
-        prepareName(expectedResolver, expectedName) 
-        RemoteRepository repositoryDummy = new RemoteRepository()
-        context.checking {
-            one(expectedResolver).setRepository(repositoryDummy)
-        }
-        assertSame(expectedResolver, repositoryHandler.mavenDeployer() {
-            setRepository(repositoryDummy)
-        });
-    }
-
-    @Test
-    public void mavenDeployerWithoutArgsAndWithClosure() {
-        GroovyMavenDeployer expectedResolver = prepareMavenDeployerTests()
-        String expectedName = "someName"
-        prepareName(expectedResolver, expectedName) 
-        RemoteRepository repositoryDummy = new RemoteRepository()
-        context.checking {
-            one(expectedResolver).setRepository(repositoryDummy)
-        }
-        assertSame(expectedResolver, repositoryHandler.mavenDeployer(name: expectedName) {
-            setRepository(repositoryDummy)
-        });
-    }
+//    @Test
+//    public void mavenDeployerWithNameAndClosure() {
+//        GroovyMavenDeployer expectedResolver = prepareMavenDeployerTests()
+//        String expectedName = RepositoryHandler.DEFAULT_MAVEN_DEPLOYER_NAME + "-" +
+//                System.identityHashCode(expectedResolver)
+//        prepareName(expectedResolver, expectedName)
+//        RemoteRepository repositoryDummy = new RemoteRepository()
+//        context.checking {
+//            one(expectedResolver).setRepository(repositoryDummy)
+//        }
+//        assertSame(expectedResolver, repositoryHandler.mavenDeployer() {
+//            setRepository(repositoryDummy)
+//        });
+//    }
+//
+//    @Test
+//    public void mavenDeployerWithoutArgsAndWithClosure() {
+//        GroovyMavenDeployer expectedResolver = prepareMavenDeployerTests()
+//        String expectedName = "someName"
+//        prepareName(expectedResolver, expectedName)
+//        RemoteRepository repositoryDummy = new RemoteRepository()
+//        context.checking {
+//            one(expectedResolver).setRepository(repositoryDummy)
+//        }
+//        assertSame(expectedResolver, repositoryHandler.mavenDeployer(name: expectedName) {
+//            setRepository(repositoryDummy)
+//        });
+//    }
 
     @Test
     public void mavenInstallerWithoutName() {
@@ -267,9 +275,62 @@ class DefaultRepositoryHandlerTest extends DefaultResolverContainerTest {
         });
     }
 
+    @Test
+    public void createIvyRepositoryUsingClosure() {
+        IvyArtifactRepository repository = context.mock(TestIvyArtifactRepository.class)
+        DependencyResolver resolver = context.mock(DependencyResolver.class)
+
+        context.checking {
+            one(resolverFactoryMock).createIvyRepository()
+            will(returnValue(repository))
+            one(repository).createResolvers(withParam(Matchers.notNullValue()))
+            will { arg -> arg << resolver }
+            one(resolverFactoryMock).createResolver(resolver)
+            will(returnValue(resolver))
+            allowing(resolver).getName()
+            will(returnValue("name"))
+        }
+
+        def arg
+        def result = repositoryHandler.ivy {
+            arg = it
+        }
+
+        assert arg == repository
+        assert result == repository
+        assert repositoryHandler.resolvers.contains(resolver)
+    }
+
+    @Test
+    public void createIvyRepositoryUsingAction() {
+        IvyArtifactRepository repository = context.mock(TestIvyArtifactRepository.class)
+        Action<IvyArtifactRepository> action = context.mock(Action.class)
+        DependencyResolver resolver = context.mock(DependencyResolver.class)
+
+        context.checking {
+            one(resolverFactoryMock).createIvyRepository()
+            will(returnValue(repository))
+            one(action).execute(repository)
+            one(repository).createResolvers(withParam(Matchers.notNullValue()))
+            will { arg -> arg << resolver }
+            one(resolverFactoryMock).createResolver(resolver)
+            will(returnValue(resolver))
+            allowing(resolver).getName()
+            will(returnValue("name"))
+        }
+
+        def result = repositoryHandler.ivy(action)
+        assert result == repository
+        assert repositoryHandler.resolvers.contains(resolver)
+    }
+    
     private void prepareName(mavenResolver, String expectedName) {
         context.checking {
             one(mavenResolver).setName(expectedName)
         }
     }
 }
+
+interface TestIvyArtifactRepository extends IvyArtifactRepository, ArtifactRepositoryInternal {
+
+}
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyDependencyResolverTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyDependencyResolverTest.java
index 16dea6b..e5168e2 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyDependencyResolverTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyDependencyResolverTest.java
@@ -118,13 +118,22 @@ public class DefaultIvyDependencyResolverTest {
         ModuleDescriptor moduleDescriptor = createAnonymousModuleDescriptor();
         prepareTestsThatRetrieveDependencies(moduleDescriptor);
 
-        Set<File> actualFiles = ivyDependencyResolver.resolve(configurationStub, ivyStub, moduleDescriptor).getFiles(
+        ResolvedConfiguration resolvedConfig = ivyDependencyResolver.resolve(configurationStub, ivyStub, moduleDescriptor);
+        Set<File> actualFiles = resolvedConfig.getFiles(
                 new Spec<Dependency>() {
                     public boolean isSatisfiedBy(Dependency element) {
                         return element == moduleDependencyDummy1 || element == selfResolvingDependencyDummy;
                     }
                 });
         assertThat(actualFiles, equalTo(toSet(new File("dep1"), new File("dep2"), new File("dep1parent"))));
+
+        Set<ResolvedDependency> actualDeps = resolvedConfig.getFirstLevelModuleDependencies(
+                new Spec<Dependency>() {
+                    public boolean isSatisfiedBy(Dependency element) {
+                        return element == moduleDependencyDummy1;
+                    }
+                });
+        assertThat(actualDeps, equalTo(toSet(resolvedDependency1, resolvedDependency2)));
     }
 
     @Test
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultResolverFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultResolverFactoryTest.groovy
deleted file mode 100644
index 5891900..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultResolverFactoryTest.groovy
+++ /dev/null
@@ -1,125 +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.internal.artifacts.ivyservice
-
-import org.apache.ivy.core.cache.DefaultRepositoryCacheManager
-import org.gradle.api.InvalidUserDataException
-import org.gradle.api.artifacts.ResolverContainer
-import org.gradle.util.JUnit4GroovyMockery
-import org.jmock.integration.junit4.JMock
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.apache.ivy.plugins.resolver.*
-import static org.junit.Assert.*
-import static org.hamcrest.Matchers.*
-import org.gradle.api.internal.Factory
-
-/**
- * @author Hans Dockter
- */
- at RunWith(JMock.class)
-class DefaultResolverFactoryTest {
-    static final String RESOLVER_URL = 'http://a.b.c/'
-    static final Map RESOLVER_MAP = [name: 'mapresolver', url: 'http://x.y.z/']
-    static final IBiblioResolver TEST_RESOLVER = new IBiblioResolver()
-    static {
-        TEST_RESOLVER.name = 'ivyResolver'
-    }
-
-    static final String TEST_REPO_NAME = 'reponame'
-    static final String TEST_REPO_URL = 'http://www.gradle.org'
-    static final File TEST_CACHE_DIR = 'somepath' as File
-
-    final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
-    final LocalMavenCacheLocator localMavenCacheLocator = context.mock(LocalMavenCacheLocator.class)
-    final DefaultResolverFactory factory = new DefaultResolverFactory(context.mock(Factory.class), localMavenCacheLocator)
-
-    @Test(expected = InvalidUserDataException) public void testCreateResolver() {
-        checkMavenResolver(factory.createResolver(RESOLVER_URL), RESOLVER_URL, RESOLVER_URL)
-        checkMavenResolver(factory.createResolver(RESOLVER_MAP), RESOLVER_MAP.name, RESOLVER_MAP.url)
-        DependencyResolver resolver = factory.createResolver(TEST_RESOLVER)
-        assert resolver.is(TEST_RESOLVER)
-        def someIllegalDescription = new NullPointerException()
-        factory.createResolver(someIllegalDescription)
-    }
-
-    private void checkMavenResolver(IBiblioResolver resolver, String name, String url) {
-        assertEquals url, resolver.root
-        assertEquals name, resolver.name
-        assertTrue resolver.allownomd
-    }
-
-    @Test
-    public void testCreateMavenRepoWithAdditionalJarUrls() {
-        String testUrl2 = 'http://www.gradle2.org'
-        DualResolver dualResolver = factory.createMavenRepoResolver(TEST_REPO_NAME, TEST_REPO_URL, testUrl2)
-        assertTrue dualResolver.allownomd
-        checkIBiblio(dualResolver.ivyResolver, "_poms")
-        URLResolver urlResolver = dualResolver.artifactResolver
-        assert urlResolver.m2compatible
-        assert urlResolver.artifactPatterns.contains("$TEST_REPO_URL/$ResolverContainer.MAVEN_REPO_PATTERN" as String)
-        assert urlResolver.artifactPatterns.contains("$testUrl2/$ResolverContainer.MAVEN_REPO_PATTERN" as String)
-        assertEquals("${TEST_REPO_NAME}_jars" as String, urlResolver.name)
-    }
-
-    @Test
-    public void testCreateMavenRepoWithNoAdditionalJarUrls() {
-        checkIBiblio(factory.createMavenRepoResolver(TEST_REPO_NAME, TEST_REPO_URL), "")
-    }
-
-    private void checkIBiblio(IBiblioResolver iBiblioResolver, String expectedNameSuffix) {
-        assert iBiblioResolver.usepoms
-        assert iBiblioResolver.m2compatible
-        assertTrue iBiblioResolver.allownomd
-        assertEquals(TEST_REPO_URL + '/', iBiblioResolver.root)
-        assertEquals(ResolverContainer.MAVEN_REPO_PATTERN, iBiblioResolver.pattern)
-        assertEquals("${TEST_REPO_NAME}$expectedNameSuffix" as String, iBiblioResolver.name)
-    }
-
-    @Test public void testCreateFlatDirResolver() {
-        File dir1 = new File('/rootFolder')
-        File dir2 = new File('/rootFolder2')
-        String expectedName = 'libs'
-        FileSystemResolver resolver = factory.createFlatDirResolver(expectedName, [dir1, dir2] as File[])
-        checkNoModuleRepository(resolver, expectedName,
-                [dir1, dir2].collect {"$it.absolutePath/$ResolverContainer.FLAT_DIR_RESOLVER_PATTERN"}, [])
-        assertEquals(new File(System.getProperty('java.io.tmpdir')).getCanonicalPath(),
-                new File(((DefaultRepositoryCacheManager) resolver.getRepositoryCacheManager()).getBasedir().getParent()).getCanonicalPath())
-
-    }
-
-    @Test public void testCreateLocalMavenRepo() {
-        File repoDir = new File(".m2/repository")
-
-        context.checking {
-            one(localMavenCacheLocator).getLocalMavenCache()
-            will(returnValue(repoDir))
-        }
-
-        def repo = factory.createMavenLocalResolver('name')
-        assertThat(repo, instanceOf(GradleIBiblioResolver))
-        assertThat(repo.root, equalTo(repoDir.toURI().toString() + '/'))
-    }
-
-    private void checkNoModuleRepository(RepositoryResolver resolver, String expectedName, List expectedArtifactPatterns,
-                                         List expectedIvyPatterns) {
-        assertEquals(expectedName, resolver.name)
-        assertEquals(expectedIvyPatterns, resolver.ivyPatterns)
-        assert expectedArtifactPatterns == resolver.artifactPatterns
-        assertTrue(resolver.allownomd)
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultSettingsConverterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultSettingsConverterTest.groovy
index fdaf37c..ca11d50 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultSettingsConverterTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultSettingsConverterTest.groovy
@@ -33,16 +33,9 @@ import static org.junit.Assert.assertSame
  * @author Hans Dockter
  */
 class DefaultSettingsConverterTest {
-    static final IBiblioResolver TEST_RESOLVER = new IBiblioResolver()
-    static final IBiblioResolver TEST_RESOLVER_2 = new IBiblioResolver()
-    static {
-        TEST_RESOLVER.name = 'resolver'
-    }
-
-    static final IBiblioResolver TEST_BUILD_RESOLVER = new IBiblioResolver()
-    static {
-        TEST_BUILD_RESOLVER.name = 'buildResolver'
-    }
+    final IBiblioResolver testResolver = new IBiblioResolver()
+    final IBiblioResolver testResolver2 = new IBiblioResolver()
+    final IBiblioResolver testBuildResolver = new IBiblioResolver()
 
     DefaultSettingsConverter converter
 
@@ -53,6 +46,8 @@ class DefaultSettingsConverterTest {
     JUnit4GroovyMockery context = new JUnit4GroovyMockery()
 
     @Before public void setUp()  {
+        testResolver.name = 'resolver'
+        testBuildResolver.name = 'buildResolver'
         context.setImposteriser(ClassImposteriser.INSTANCE)
         converter = new DefaultSettingsConverter()
         clientModuleRegistry = [a: [:] as ModuleDescriptor]
@@ -60,13 +55,13 @@ class DefaultSettingsConverterTest {
     }
 
     @Test public void testConvertForResolve() {
-        IvySettings settings = converter.convertForResolve([TEST_RESOLVER, TEST_RESOLVER_2], testGradleUserHome,
-                TEST_BUILD_RESOLVER, clientModuleRegistry)
+        IvySettings settings = converter.convertForResolve([testResolver, testResolver2], testGradleUserHome,
+                testBuildResolver, clientModuleRegistry)
         ChainResolver chainResolver = settings.getResolver(DefaultSettingsConverter.CHAIN_RESOLVER_NAME)
         assertEquals(3, chainResolver.resolvers.size())
-        assert chainResolver.resolvers[0].name.is(TEST_BUILD_RESOLVER.name)
-        assert chainResolver.resolvers[1].is(TEST_RESOLVER)
-        assert chainResolver.resolvers[2].is(TEST_RESOLVER_2)
+        assert chainResolver.resolvers[0].name.is(testBuildResolver.name)
+        assert chainResolver.resolvers[1].is(testResolver)
+        assert chainResolver.resolvers[2].is(testResolver2)
         assertTrue chainResolver.returnFirst
 
         ClientModuleResolver clientModuleResolver = settings.getResolver(DefaultSettingsConverter.CLIENT_MODULE_NAME)
@@ -76,11 +71,12 @@ class DefaultSettingsConverterTest {
         assert clientModuleChain.resolvers[1].is(chainResolver)
         assert settings.defaultResolver.is(clientModuleChain)
 
-        [TEST_BUILD_RESOLVER.name, TEST_RESOLVER.name, TEST_RESOLVER_2.name, DefaultSettingsConverter.CHAIN_RESOLVER_NAME,
-                DefaultSettingsConverter.CLIENT_MODULE_NAME, DefaultSettingsConverter.CLIENT_MODULE_CHAIN_NAME].each {
+        [testBuildResolver.name, testResolver.name, testResolver2.name, DefaultSettingsConverter.CHAIN_RESOLVER_NAME,
+                DefaultSettingsConverter.CLIENT_MODULE_CHAIN_NAME].each {
             assert settings.getResolver(it)
             assert settings.getResolver(it).getRepositoryCacheManager().settings == settings
         }
+        assert settings.getResolver(DefaultSettingsConverter.CLIENT_MODULE_NAME)
 
         assertEquals(new File(testGradleUserHome, ResolverContainer.DEFAULT_CACHE_DIR_NAME),
                 settings.defaultCache)
@@ -88,28 +84,16 @@ class DefaultSettingsConverterTest {
     }
 
     @Test public void testConvertForPublish() {
-        IvySettings settings = converter.convertForPublish([TEST_RESOLVER, TEST_RESOLVER_2], testGradleUserHome,
-                TEST_BUILD_RESOLVER)
-        ChainResolver chainResolver = settings.getResolver(DefaultSettingsConverter.CHAIN_RESOLVER_NAME)
-        assertEquals(1, chainResolver.resolvers.size())
-        assert chainResolver.resolvers[0].name.is(TEST_BUILD_RESOLVER.name)
-        assertTrue chainResolver.returnFirst
-
-        ClientModuleResolver clientModuleResolver = settings.getResolver(DefaultSettingsConverter.CLIENT_MODULE_NAME)
-        ChainResolver clientModuleChain = settings.getResolver(DefaultSettingsConverter.CLIENT_MODULE_CHAIN_NAME)
-        assertTrue clientModuleChain.returnFirst
-        assert clientModuleChain.resolvers[0].is(clientModuleResolver)
-        assert clientModuleChain.resolvers[1].is(chainResolver)
-        assert settings.defaultResolver.is(clientModuleChain)
+        IvySettings settings = converter.convertForPublish([testResolver, testResolver2], testGradleUserHome,
+                testBuildResolver)
 
-        [TEST_BUILD_RESOLVER.name, TEST_RESOLVER.name, TEST_RESOLVER_2.name, DefaultSettingsConverter.CHAIN_RESOLVER_NAME,
-                DefaultSettingsConverter.CLIENT_MODULE_NAME, DefaultSettingsConverter.CLIENT_MODULE_CHAIN_NAME].each {
+        [testResolver.name, testResolver2.name].each {
             assert settings.getResolver(it)
             assert settings.getResolver(it).getRepositoryCacheManager().settings == settings
         }
 
-        assert settings.getResolver(TEST_RESOLVER.name).is(TEST_RESOLVER)
-        assert settings.getResolver(TEST_RESOLVER_2.name).is(TEST_RESOLVER_2)
+        assert settings.getResolver(testResolver.name).is(testResolver)
+        assert settings.getResolver(testResolver2.name).is(testResolver2)
         assertEquals(new File(testGradleUserHome, ResolverContainer.DEFAULT_CACHE_DIR_NAME),
                 settings.defaultCache)
         assertEquals(settings.defaultCacheArtifactPattern, ResolverContainer.DEFAULT_CACHE_ARTIFACT_PATTERN)
@@ -117,12 +101,12 @@ class DefaultSettingsConverterTest {
 
     @Test
     public void repositoryCacheManagerShouldBeSharedBetweenSettings() {
-        IvySettings settings1 = converter.convertForPublish([TEST_RESOLVER, TEST_RESOLVER_2], testGradleUserHome,
-                TEST_BUILD_RESOLVER)
-        IvySettings settings2 = converter.convertForPublish([TEST_RESOLVER, TEST_RESOLVER_2], testGradleUserHome,
-                TEST_BUILD_RESOLVER)
-        IvySettings settings3 = converter.convertForResolve([TEST_RESOLVER, TEST_RESOLVER_2], testGradleUserHome,
-                TEST_BUILD_RESOLVER, clientModuleRegistry)
+        IvySettings settings1 = converter.convertForPublish([testResolver, testResolver2], testGradleUserHome,
+                testBuildResolver)
+        IvySettings settings2 = converter.convertForPublish([testResolver, testResolver2], testGradleUserHome,
+                testBuildResolver)
+        IvySettings settings3 = converter.convertForResolve([testResolver, testResolver2], testGradleUserHome,
+                testBuildResolver, clientModuleRegistry)
         assertSame(settings1.getDefaultRepositoryCacheManager(), settings2.getDefaultRepositoryCacheManager())
         assertSame(settings1.getDefaultRepositoryCacheManager(), settings3.getDefaultRepositoryCacheManager())
 
@@ -131,9 +115,9 @@ class DefaultSettingsConverterTest {
     @Test public void testWithGivenSettings() {
         IvySettings ivySettings = [:] as IvySettings
         converter.ivySettings = ivySettings
-        assert ivySettings.is(converter.convertForResolve([TEST_RESOLVER], new File(''),
-                TEST_BUILD_RESOLVER, clientModuleRegistry))
-        assert ivySettings.is(converter.convertForPublish([TEST_RESOLVER], new File(''),
-                TEST_BUILD_RESOLVER))
+        assert ivySettings.is(converter.convertForResolve([testResolver], new File(''),
+                testBuildResolver, clientModuleRegistry))
+        assert ivySettings.is(converter.convertForPublish([testResolver], new File(''),
+                testBuildResolver))
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingIvyServiceTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingIvyServiceTest.groovy
index 100ab39..9b4eaac 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingIvyServiceTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingIvyServiceTest.groovy
@@ -16,19 +16,17 @@
 package org.gradle.api.internal.artifacts.ivyservice;
 
 
-import org.gradle.api.GradleException
-import org.gradle.api.artifacts.Configuration
-import org.gradle.api.artifacts.Dependency
-import org.gradle.api.artifacts.ResolveException
-import org.gradle.api.artifacts.ResolvedConfiguration
 import org.gradle.api.internal.artifacts.IvyService
 import org.gradle.api.specs.Spec
 import org.gradle.util.JUnit4GroovyMockery
 import org.jmock.integration.junit4.JMock
 import org.junit.Test
 import org.junit.runner.RunWith
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
+import org.gradle.api.artifacts.*
+import static org.hamcrest.Matchers.equalTo
+import static org.hamcrest.Matchers.sameInstance
+import static org.junit.Assert.assertThat
+import static org.junit.Assert.fail
 
 @RunWith(JMock.class)
 public class ErrorHandlingIvyServiceTest {
@@ -140,7 +138,7 @@ public class ErrorHandlingIvyServiceTest {
             ivyService.publish([configurationMock] as Set, null, [])
             fail()
         }
-        catch(GradleException e) {
+        catch(PublishException e) {
             assertThat e.message, equalTo("Could not publish configurations [<config display name>].")
             assertThat(e.cause, sameInstance((Throwable) failure));
         }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/LocalMavenCacheLocatorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/LocalMavenCacheLocatorTest.groovy
deleted file mode 100644
index fbc536e..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/LocalMavenCacheLocatorTest.groovy
+++ /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.api.internal.artifacts.ivyservice
-
-import spock.lang.Specification
-import org.junit.Rule
-import org.gradle.util.SetSystemProperties
-import org.gradle.util.TemporaryFolder
-
-class LocalMavenCacheLocatorTest extends Specification {
-    @Rule public final SetSystemProperties systemProperties = new SetSystemProperties()
-    @Rule public final TemporaryFolder tmpDir = new TemporaryFolder()
-    final LocalMavenCacheLocator locator = new LocalMavenCacheLocator()
-
-    def setup() {
-        System.setProperty('user.home', tmpDir.dir.absolutePath)
-    }
-
-    def usesDefaultWhenNoSettingsXmlFile() {
-        expect:
-        locator.localMavenCache == new File(tmpDir.dir, '.m2/repository')
-    }
-
-    def usesValueFromSettingsXmlFile() {
-        tmpDir.file('.m2/settings.xml') << """
-<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-  <localRepository>${tmpDir.file('.m2/custom').absolutePath}</localRepository>
-</settings>"""
-
-        expect:
-        locator.localMavenCache == new File(tmpDir.dir, '.m2/custom')
-    }
-
-    def usesValueWithPlaceholderFromSettingsXmlFile() {
-        tmpDir.file('.m2/settings.xml') << '''
-<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-  <localRepository>${user.home}/.m2/custom</localRepository>
-</settings>'''
-
-        expect:
-        locator.localMavenCache == new File(tmpDir.dir, '.m2/custom')
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/SelfResolvingDependencyResolverTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/SelfResolvingDependencyResolverTest.java
index 0143013..7a0bf24 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/SelfResolvingDependencyResolverTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/SelfResolvingDependencyResolverTest.java
@@ -38,9 +38,10 @@ import java.io.File;
 import java.io.IOException;
 import java.text.ParseException;
 
-import static org.gradle.util.WrapUtil.*;
+import static org.gradle.util.WrapUtil.toLinkedSet;
+import static org.gradle.util.WrapUtil.toSet;
 import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertThat;
 
 @RunWith(JMock.class)
 public class SelfResolvingDependencyResolverTest {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomFactoryTest.groovy
deleted file mode 100644
index d8a4b0a..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomFactoryTest.groovy
+++ /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.internal.artifacts.publish.maven;
-
-
-import org.gradle.api.artifacts.ConfigurationContainer
-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
-import spock.lang.Specification
-
-/**
- * @author Hans Dockter
- */
-public class DefaultMavenPomFactoryTest extends Specification {
-    def createMavenPom() {
-        DefaultConf2ScopeMappingContainer scopeMappings = new DefaultConf2ScopeMappingContainer();
-        PomDependenciesConverter pomDependenciesConverter = Mock(PomDependenciesConverter); 
-        ConfigurationContainer configurationContainer = Mock(ConfigurationContainer); 
-        FileResolver fileResolver = Mock(FileResolver); 
-        DefaultMavenPomFactory mavenPomFactory = new DefaultMavenPomFactory(configurationContainer, scopeMappings,
-                pomDependenciesConverter, fileResolver);
-        DefaultMavenPom mavenPom = (DefaultMavenPom) mavenPomFactory.create();
-
-        expect:
-        !scopeMappings.is(mavenPom.scopeMappings)
-        scopeMappings == mavenPom.scopeMappings
-        mavenPom.mavenProject != null
-        mavenPom.pomDependenciesConverter.is(pomDependenciesConverter)
-        mavenPom.configurations.is(configurationContainer)
-        mavenPom.fileResolver == fileResolver
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomTest.groovy
deleted file mode 100644
index c6cf002..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomTest.groovy
+++ /dev/null
@@ -1,191 +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.apache.commons.lang.builder.EqualsBuilder
-import org.apache.maven.model.Dependency
-import org.apache.maven.model.Model
-import org.gradle.api.Action
-import org.gradle.api.artifacts.Configuration
-import org.gradle.api.artifacts.ConfigurationContainer
-import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer
-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
-import org.gradle.util.TemporaryFolder
-import org.gradle.util.TestFile
-import org.gradle.util.TextUtil
-import org.junit.Rule
-import spock.lang.Specification
-
-class DefaultMavenPomTest extends Specification {
-    static final String EXPECTED_PACKAGING = "something";
-    static final String EXPECTED_GROUP_ID = "someGroup";
-    static final String EXPECTED_ARTIFACT_ID = "artifactId";
-    static final String EXPECTED_VERSION = "version";
-
-    @Rule
-    TemporaryFolder tmpDir = new TemporaryFolder()
-
-    Conf2ScopeMappingContainer conf2ScopeMappingContainer = new DefaultConf2ScopeMappingContainer()
-    PomDependenciesConverter pomDependenciesConverterStub = Mock()
-    ConfigurationContainer configurationContainerStub = Mock()
-    FileResolver fileResolver = Mock()
-    DefaultMavenPom mavenPom = new DefaultMavenPom(configurationContainerStub, conf2ScopeMappingContainer, pomDependenciesConverterStub,
-            fileResolver)
-
-    void setup() {
-        mavenPom.packaging = EXPECTED_PACKAGING
-        mavenPom.groupId = EXPECTED_GROUP_ID
-        mavenPom.artifactId = EXPECTED_ARTIFACT_ID
-        mavenPom.version = EXPECTED_VERSION
-    }
-
-    def init() {
-        expect:
-        mavenPom.scopeMappings.is(conf2ScopeMappingContainer)
-        mavenPom.configurations.is(configurationContainerStub)
-        mavenPom.fileResolver.is(fileResolver)
-        mavenPom.mavenProject.modelVersion == "4.0.0"
-    }
-
-    def setModel() {
-        def newModel = new Model()
-
-        when:
-        mavenPom.model = newModel
-
-        then:
-        mavenPom.model.is(newModel)
-    }
-
-    def effectivePomShouldHaveGeneratedDependencies() {
-        Set configurations = [Mock(Configuration)]
-        configurationContainerStub.getAll() >> configurations
-        List generatedDependencies = [new Dependency(groupId: 'someGroup')]
-        List manuallyAddedDependencies = [new Dependency()]
-        pomDependenciesConverterStub.convert(conf2ScopeMappingContainer, configurations) >> generatedDependencies
-
-        when:
-        mavenPom.dependencies = manuallyAddedDependencies.clone()
-
-        then:
-        EqualsBuilder.reflectionEquals(mavenPom.getEffectivePom().getMavenProject().getDependencies(), manuallyAddedDependencies + generatedDependencies)
-
-        when:
-        mavenPom.dependencies = []
-
-        then:
-        mavenPom.getEffectivePom().getMavenProject().getDependencies() == generatedDependencies
-    }
-
-    def configureActionsShouldBeAppliedAgainstEffectivePom() {
-        mavenPom.configurations = null
-        when:
-        mavenPom.whenConfigured(new Action() {
-            void execute(def mavenPom) {
-                mavenPom.mavenProject.inceptionYear = '1999'
-            }
-        })
-
-        then:
-        mavenPom.effectivePom.mavenProject.inceptionYear == '1999'
-        mavenPom.mavenProject.inceptionYear == null
-    }
-
-
-    def writeShouldUseEffectivePom() {
-        Set configurations = [Mock(Configuration)]
-        configurationContainerStub.getAll() >> configurations
-        List generatedDependencies = [new Dependency(groupId: 'someGroup')]
-        pomDependenciesConverterStub.convert(conf2ScopeMappingContainer, configurations) >> generatedDependencies
-
-        when:
-        StringWriter pomWriter = new StringWriter()
-        mavenPom.writeTo pomWriter
-
-        then:
-        pomWriter.toString().contains('someGroup')
-    }
-
-    def effectivePomWithNullConfigurationsShouldWork() {
-        when:
-        mavenPom.configurations = null
-
-        then:
-        mavenPom.getEffectivePom().getMavenProject().getDependencies() == []
-    }
-
-    void projectBuilder() {
-        mavenPom.mavenProject.inceptionYear = '2007'
-        mavenPom.mavenProject.description = 'some description'
-        mavenPom.project {
-            inceptionYear '2008'
-            licenses {
-                license {
-                    name 'The Apache Software License, Version 2.0'
-                    url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                    distribution 'repo'
-                }
-            }
-        }
-
-        expect:
-        mavenPom.mavenProject.modelVersion == "4.0.0"
-        mavenPom.version == EXPECTED_VERSION
-        mavenPom.mavenProject.description == 'some description'
-        mavenPom.mavenProject.inceptionYear == '2008'
-        mavenPom.mavenProject.licenses.size() == 1
-        mavenPom.mavenProject.licenses[0].name == 'The Apache Software License, Version 2.0'
-        mavenPom.mavenProject.licenses[0].url == 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-        mavenPom.mavenProject.licenses[0].distribution == 'repo'
-    }
-
-    void writeToShouldApplyXmlActions() {
-        mavenPom.configurations = null
-        StringWriter pomWriter = new StringWriter()
-
-        when:
-        mavenPom.withXml {xmlProvider ->
-            xmlProvider.asString().append('someAppendix')
-        }
-        mavenPom.writeTo(pomWriter);
-
-        then:
-        pomWriter.toString().endsWith("someAppendix")
-    }
-
-    void writeToWritesCorrectPom() {
-        mavenPom.configurations = null
-        TestFile pomFile = tmpDir.file('someNonexistingDir').file('someFile')
-        fileResolver.resolve('file') >> pomFile
-
-        when:
-        mavenPom.writeTo('file');
-
-        then:
-        pomFile.text == TextUtil.toNativeLineSeparators('''<?xml version="1.0" encoding="UTF-8"?>
-<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-  <modelVersion>4.0.0</modelVersion>
-  <groupId>someGroup</groupId>
-  <artifactId>artifactId</artifactId>
-  <version>version</version>
-  <packaging>something</packaging>
-</project>
-''')
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DeployTaskFactoryTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DeployTaskFactoryTest.java
deleted file mode 100644
index 424e4af..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DeployTaskFactoryTest.java
+++ /dev/null
@@ -1,31 +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 static junit.framework.Assert.assertTrue;
-import org.gradle.api.internal.artifacts.publish.maven.deploy.CustomDeployTask;
-import org.gradle.api.internal.artifacts.publish.maven.deploy.DefaultDeployTaskFactory;
-import org.junit.Test;
-
-/**
- * @author Hans Dockter
- */
-public class DeployTaskFactoryTest {
-    @Test
-    public void create() {
-        assertTrue(new DefaultDeployTaskFactory().create() instanceof CustomDeployTask);
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultPomDependenciesConverterTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultPomDependenciesConverterTest.java
deleted file mode 100644
index 78bfc8d..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/dependencies/DefaultPomDependenciesConverterTest.java
+++ /dev/null
@@ -1,253 +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.artifacts.publish.maven.dependencies;
-
-import static java.util.Arrays.asList;
-import static org.gradle.util.WrapUtil.toMap;
-import static org.gradle.util.WrapUtil.toSet;
-import static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.apache.maven.model.Exclusion;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.Dependency;
-import org.gradle.api.artifacts.ExcludeRule;
-import org.gradle.api.artifacts.ModuleDependency;
-import org.gradle.api.artifacts.maven.Conf2ScopeMapping;
-import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
-import org.gradle.api.internal.artifacts.DefaultExcludeRule;
-import org.gradle.api.internal.artifacts.dependencies.DefaultDependencyArtifact;
-import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency;
-import org.gradle.util.WrapUtil;
-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;
-
-/**
- * @author Hans Dockter
- */
- at RunWith(JMock.class)
-public class DefaultPomDependenciesConverterTest {
-    private JUnit4Mockery context = new JUnit4Mockery();
-    
-    private DefaultPomDependenciesConverter dependenciesConverter;
-    private Conf2ScopeMappingContainer conf2ScopeMappingContainerMock = context.mock(Conf2ScopeMappingContainer.class);
-    private ExcludeRuleConverter excludeRuleConverterMock = context.mock(ExcludeRuleConverter.class);
-
-    private ModuleDependency dependency1;
-    private ModuleDependency dependency2;
-    private ModuleDependency dependency31;
-    private ModuleDependency dependency32;
-    private Configuration compileConfStub;
-    private Configuration testCompileConfStub;
-
-    @Before
-    public void setUp() {
-        setUpCommonDependenciesAndConfigurations();
-        dependenciesConverter = new DefaultPomDependenciesConverter(excludeRuleConverterMock);
-    }
-
-    private void setUpCommonDependenciesAndConfigurations() {
-        dependency1 = createDependency("org1", "name1", "rev1");
-        dependency2 = createDependency("org2", "name2", "rev2");
-        dependency2.addArtifact(new DefaultDependencyArtifact("name2", null, null, null, null));
-        dependency31 = createDependency("org3", "name3", "rev3");
-        dependency32 = createDependency("org3", "name3", "rev3");
-        dependency32.addArtifact(new DefaultDependencyArtifact("artifactName32", "type32", "ext", "classifier32", null));
-        compileConfStub = createNamedConfigurationStubWithDependencies("compile", dependency1, dependency31);
-        testCompileConfStub = createNamedConfigurationStubWithDependencies("testCompile", dependency2, dependency32);
-        context.checking(new Expectations() {{
-            allowing(conf2ScopeMappingContainerMock).getMapping(toSet(testCompileConfStub, compileConfStub)); will(returnValue(createMapping(testCompileConfStub, "test")));
-            allowing(conf2ScopeMappingContainerMock).getMapping(toSet(compileConfStub, testCompileConfStub)); will(returnValue(createMapping(testCompileConfStub, "test")));
-            allowing(conf2ScopeMappingContainerMock).getMapping(toSet(testCompileConfStub)); will(returnValue(createMapping(testCompileConfStub, "test")));
-            allowing(conf2ScopeMappingContainerMock).getMapping(toSet(compileConfStub)); will(returnValue(createMapping(compileConfStub, "compile")));
-        }});
-    }
-
-    private Conf2ScopeMapping createMapping(Configuration configuration, String scope) {
-        return new Conf2ScopeMapping(10, configuration, scope);
-    }
-
-    private Configuration createNamedConfigurationStubWithDependencies(final String confName, final ModuleDependency... dependencies) {
-        return createNamedConfigurationStubWithDependencies(confName, new HashSet<ExcludeRule>(), dependencies);
-    }
-    
-    private Configuration createNamedConfigurationStubWithDependencies(final String confName, final Set<ExcludeRule> excludeRules, final ModuleDependency... dependencies) {
-        final Configuration configurationStub = context.mock(Configuration.class, confName);
-        context.checking(new Expectations() {{
-            allowing(configurationStub).getName();
-            will(returnValue(confName));
-            allowing(configurationStub).getDependencies(ModuleDependency.class);
-            will(returnValue(toSet(dependencies)));
-            allowing(configurationStub).getExcludeRules();
-            will(returnValue(excludeRules));            
-        }});
-        return configurationStub;
-    }
-
-    private ModuleDependency createDependency(final String group, final String name, final String version) {
-        return new DefaultExternalModuleDependency(group, name, version);
-    }
-
-    @Test
-    public void init() {
-        assertSame(excludeRuleConverterMock, dependenciesConverter.getExcludeRuleConverter());
-    }
-
-    @Test
-    public void convert() {
-        Set<Configuration> configurations = toSet(compileConfStub, testCompileConfStub);
-        context.checking(new Expectations() {{
-            allowing(conf2ScopeMappingContainerMock).isSkipUnmappedConfs(); will(returnValue(false));
-        }});
-        List<org.apache.maven.model.Dependency> actualMavenDependencies = dependenciesConverter.convert(conf2ScopeMappingContainerMock, configurations);
-        assertEquals(3, actualMavenDependencies.size());
-        checkCommonMavenDependencies(actualMavenDependencies);
-    }
-
-    @Test
-    public void convertWithUnMappedConfAndSkipTrue() {
-        final Dependency dependency4 = createDependency("org4", "name4", "rev4");
-        final Configuration unmappedConfigurationStub = createNamedConfigurationStubWithDependencies("unmappedConf");
-        context.checking(new Expectations() {{
-            allowing(unmappedConfigurationStub).getDependencies();
-            will(returnValue(toSet(dependency4)));
-        }});
-        context.checking(new Expectations() {{
-            allowing(conf2ScopeMappingContainerMock).isSkipUnmappedConfs(); will(returnValue(true));
-            allowing(conf2ScopeMappingContainerMock).getMapping(asList(unmappedConfigurationStub)); will(returnValue(null));
-        }});
-        List<org.apache.maven.model.Dependency> actualMavenDependencies = dependenciesConverter.convert(conf2ScopeMappingContainerMock, toSet(
-                compileConfStub, testCompileConfStub, unmappedConfigurationStub));
-        assertEquals(3, actualMavenDependencies.size());
-        checkCommonMavenDependencies(actualMavenDependencies);
-    }
-
-    @Test
-    public void convertWithUnMappedConfAndSkipFalse() {
-        final ModuleDependency dependency4 = createDependency("org4", "name4", "rev4");
-        final Configuration unmappedConfigurationStub = createNamedConfigurationStubWithDependencies("unmappedConf", dependency4);
-        context.checking(new Expectations() {{
-            allowing(conf2ScopeMappingContainerMock).isSkipUnmappedConfs(); will(returnValue(false));
-            allowing(conf2ScopeMappingContainerMock).getMapping(toSet(unmappedConfigurationStub)); will(returnValue(new Conf2ScopeMapping(null, unmappedConfigurationStub, null)));
-        }});
-        List<org.apache.maven.model.Dependency> actualMavenDependencies = dependenciesConverter.convert(conf2ScopeMappingContainerMock, toSet(
-                compileConfStub, testCompileConfStub, unmappedConfigurationStub));
-        assertEquals(4, actualMavenDependencies.size());
-        checkCommonMavenDependencies(actualMavenDependencies);
-        assertTrue(hasDependency(actualMavenDependencies, "org4", "name4", "rev4", null, null, null, false));
-    }
-
-    private void checkCommonMavenDependencies(List<org.apache.maven.model.Dependency> actualMavenDependencies) {
-        assertTrue(hasDependency(actualMavenDependencies, "org1", "name1", "rev1", null, "compile", null, false));
-        assertTrue(hasDependency(actualMavenDependencies, "org2", "name2", "rev2", null, "test", null, false));
-        assertTrue(hasDependency(actualMavenDependencies, "org3", "artifactName32", "rev3", "type32", "test", "classifier32", false));
-    }
-
-    private boolean hasDependency(List<org.apache.maven.model.Dependency> mavenDependencies,
-                                  String group, String artifactId, String version, String type, String scope,
-                                  String classifier, boolean optional) {
-        org.apache.maven.model.Dependency expectedDependency = new org.apache.maven.model.Dependency();
-        expectedDependency.setGroupId(group);
-        expectedDependency.setArtifactId(artifactId);
-        expectedDependency.setVersion(version);
-        expectedDependency.setType(type);
-        expectedDependency.setScope(scope);
-        expectedDependency.setClassifier(classifier);
-        expectedDependency.setOptional(optional);
-        for (org.apache.maven.model.Dependency mavenDependency : mavenDependencies) {
-            if (equals(mavenDependency, expectedDependency)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private boolean equals(org.apache.maven.model.Dependency lhs, org.apache.maven.model.Dependency rhs) {
-        if (!lhs.getGroupId().equals(lhs.getGroupId())) {
-            return false;
-        }
-        if (!lhs.getArtifactId().equals(lhs.getArtifactId())) {
-            return false;
-        }
-        if (!lhs.getVersion().equals(lhs.getVersion())) {
-            return false;
-        }
-        if (lhs.getType() != null ? !lhs.getType().equals(lhs.getType()) : rhs.getType() != null) {
-            return false;
-        }
-        if (lhs.getScope() != null ? !lhs.getScope().equals(lhs.getScope()) : rhs.getScope() != null) {
-            return false;
-        }
-        if (!lhs.isOptional() == lhs.isOptional()) {
-            return false;
-        }
-        if (lhs.getClassifier() != null ? !lhs.getClassifier().equals(rhs.getClassifier()) : rhs.getClassifier() != null) {
-            return false;
-        }
-        return true;
-    }
-
-    @Test
-    public void convertWithConvertableDependencyExcludes() {
-        final Configuration someConfigurationStub = createNamedConfigurationStubWithDependencies("someConfiguration", dependency1);
-        final Exclusion mavenExclude = new Exclusion();
-        mavenExclude.setGroupId("a");
-        mavenExclude.setArtifactId("b");
-        dependency1.exclude(toMap("key", "value"));
-        context.checking(new Expectations() {{
-           allowing(conf2ScopeMappingContainerMock).getMapping(toSet(someConfigurationStub)); will(returnValue(createMapping(compileConfStub, "compile")));
-           allowing(excludeRuleConverterMock).convert(dependency1.getExcludeRules().iterator().next()); will(returnValue(mavenExclude));
-        }});
-        List<org.apache.maven.model.Dependency> actualMavenDependencies = dependenciesConverter.convert(conf2ScopeMappingContainerMock, toSet(someConfigurationStub));
-        assertEquals(1, actualMavenDependencies.size());
-        assertTrue(hasDependency(actualMavenDependencies, "org1", "name1", "rev1", null, "compile", null, false));
-        org.apache.maven.model.Dependency mavenDependency = (org.apache.maven.model.Dependency) actualMavenDependencies.get(0);
-        assertThat(mavenDependency.getExclusions().size(), equalTo(1));
-        assertThat(((Exclusion) mavenDependency.getExclusions().get(0)).getGroupId(), equalTo(mavenExclude.getGroupId()));
-        assertThat(((Exclusion) mavenDependency.getExclusions().get(0)).getArtifactId(), equalTo(mavenExclude.getArtifactId()));
-    }
-    
-    @Test
-    public void convertWithConvertableConfigurationExcludes() {
-        final Configuration someConfigurationStub = createNamedConfigurationStubWithDependencies("someConfiguration", 
-                WrapUtil.<ExcludeRule>toSet(new DefaultExcludeRule(toMap("key", "value"))), dependency1);
-        final Exclusion mavenExclude = new Exclusion();
-        mavenExclude.setGroupId("a");
-        mavenExclude.setArtifactId("b");
-        context.checking(new Expectations() {{
-           allowing(conf2ScopeMappingContainerMock).getMapping(toSet(someConfigurationStub)); will(returnValue(createMapping(compileConfStub, "compile")));
-           allowing(excludeRuleConverterMock).convert(someConfigurationStub.getExcludeRules().iterator().next()); will(returnValue(mavenExclude));
-        }});
-        List<org.apache.maven.model.Dependency> actualMavenDependencies = dependenciesConverter.convert(conf2ScopeMappingContainerMock, toSet(someConfigurationStub));
-        assertEquals(1, actualMavenDependencies.size());
-        assertTrue(hasDependency(actualMavenDependencies, "org1", "name1", "rev1", null, "compile", null, false));
-        org.apache.maven.model.Dependency mavenDependency = (org.apache.maven.model.Dependency) actualMavenDependencies.get(0);
-        assertThat(mavenDependency.getExclusions().size(), equalTo(1));
-        assertThat(((Exclusion) mavenDependency.getExclusions().get(0)).getGroupId(), equalTo(mavenExclude.getGroupId()));
-        assertThat(((Exclusion) mavenDependency.getExclusions().get(0)).getArtifactId(), equalTo(mavenExclude.getArtifactId()));
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/AbstractMavenResolverTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/AbstractMavenResolverTest.java
deleted file mode 100644
index 89d52b2..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/AbstractMavenResolverTest.java
+++ /dev/null
@@ -1,258 +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;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.ivy.core.module.descriptor.Artifact;
-import org.apache.ivy.core.module.descriptor.DefaultArtifact;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.apache.maven.artifact.ant.AttachedArtifact;
-import org.apache.maven.artifact.ant.InstallDeployTaskSupport;
-import org.apache.maven.artifact.ant.Pom;
-import org.apache.maven.settings.Settings;
-import org.apache.tools.ant.Project;
-import org.codehaus.plexus.PlexusContainerException;
-import org.gradle.api.Action;
-import org.gradle.api.artifacts.PublishArtifact;
-import org.gradle.api.artifacts.maven.*;
-import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact;
-import org.gradle.api.logging.LogLevel;
-import org.gradle.logging.LoggingManagerInternal;
-import org.gradle.util.AntUtil;
-import org.gradle.util.JUnit4GroovyMockery;
-import org.gradle.util.WrapUtil;
-import org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
-import org.jmock.Expectations;
-import org.jmock.api.Invocation;
-import org.jmock.lib.legacy.ClassImposteriser;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Collections;
-import java.util.Set;
-
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-/**
- * @author Hans Dockter
- */
-public abstract class AbstractMavenResolverTest {
-    public static final String TEST_NAME = "name";
-    private static final Artifact TEST_IVY_ARTIFACT = DefaultArtifact.newIvyArtifact(ModuleRevisionId.newInstance("org", TEST_NAME, "1.0"), null);
-    private static final File TEST_IVY_FILE = new File("somepom.xml");
-    private static final File TEST_JAR_FILE = new File("somejar.jar");
-    private static final Artifact TEST_ARTIFACT = new DefaultArtifact(ModuleRevisionId.newInstance("org", TEST_NAME, "1.0"), null, TEST_NAME, "jar", "jar");
-    protected ArtifactPomContainer artifactPomContainerMock;
-    protected PomFilterContainer pomFilterContainerMock;
-    protected LoggingManagerInternal loggingManagerMock;
-
-    protected JUnit4GroovyMockery context = new JUnit4GroovyMockery() {
-        {
-            setImposteriser(ClassImposteriser.INSTANCE);
-        }
-    };
-    protected MavenPom pomMock;
-
-    protected Settings mavenSettingsMock;
-
-    protected abstract MavenResolver getMavenResolver();
-
-    protected abstract InstallDeployTaskSupport getInstallDeployTask();
-
-    protected abstract PomFilterContainer createPomFilterContainerMock();
-
-    @Before
-    public void setUp() {
-        pomFilterContainerMock = createPomFilterContainerMock();
-        artifactPomContainerMock = context.mock(ArtifactPomContainer.class);
-        pomMock = context.mock(MavenPom.class);
-        mavenSettingsMock = context.mock(Settings.class);
-        loggingManagerMock = context.mock(LoggingManagerInternal.class);
-    }
-
-    @Test
-    public void deployOrInstall() throws IOException, PlexusContainerException {
-        PublishArtifact classifierArtifact = artifact(new File("classifier.jar"));
-        final DefaultMavenDeployment deployment1 = new DefaultMavenDeployment(artifact(new File("pom1.pom")), artifact(new File("artifact1.jar")), Collections.<PublishArtifact>emptySet());
-        final DefaultMavenDeployment deployment2 = new DefaultMavenDeployment(artifact(new File("pom2.pom")), artifact(new File("artifact2.jar")), WrapUtil.toSet(classifierArtifact));
-        final Set<DefaultMavenDeployment> testDefaultMavenDeployments = WrapUtil.toSet(
-                deployment1,
-                deployment2
-        );
-        final AttachedArtifact attachedArtifact = new AttachedArtifact();
-        final Action<MavenDeployment> action = context.mock(Action.class);
-
-        context.checking(new Expectations() {
-            {
-                allowing((CustomInstallDeployTaskSupport) getInstallDeployTask()).getSettings();
-                will(returnValue(mavenSettingsMock));
-                allowing((CustomInstallDeployTaskSupport) getInstallDeployTask()).getProject();
-                will(returnValue(AntUtil.createProject()));
-                allowing((CustomInstallDeployTaskSupport) getInstallDeployTask()).createAttach();
-                will(returnValue(attachedArtifact));
-                one(artifactPomContainerMock).addArtifact(TEST_ARTIFACT, TEST_JAR_FILE);
-                allowing(artifactPomContainerMock).createDeployableFilesInfos();
-                will(returnValue(testDefaultMavenDeployments));
-                one(action).execute(deployment1);
-                one(action).execute(deployment2);
-            }
-        });
-
-        getMavenResolver().beforeDeployment(action);
-        getMavenResolver().publish(TEST_IVY_ARTIFACT, TEST_IVY_FILE, true);
-        getMavenResolver().publish(TEST_ARTIFACT, TEST_JAR_FILE, true);
-        checkTransaction(testDefaultMavenDeployments, attachedArtifact, classifierArtifact);
-        assertSame(mavenSettingsMock, getMavenResolver().getSettings());
-    }
-
-    private PublishArtifact artifact(File file) {
-        return new DefaultPublishArtifact("name", "ext", "type", null, null, file);
-    }
-
-    protected void checkTransaction(final Set<DefaultMavenDeployment> defaultMavenDeployments, final AttachedArtifact attachedArtifact, PublishArtifact classifierArtifact) throws IOException, PlexusContainerException {
-        final GrabSettingsFileAction grabSettingsFileAction = new GrabSettingsFileAction();
-        context.checking(new Expectations() {
-            {
-                one(getInstallDeployTask()).setProject(with(any(Project.class)));
-                one(getInstallDeployTask()).setSettingsFile(with(any(File.class)));
-                will(grabSettingsFileAction);
-                for (DefaultMavenDeployment defaultMavenDeployment : defaultMavenDeployments) {
-                    one(getInstallDeployTask()).setFile(defaultMavenDeployment.getMainArtifact().getFile());
-                    one(getInstallDeployTask()).addPom(with(pomMatcher(defaultMavenDeployment.getPomArtifact().getFile(), getInstallDeployTask().getProject())));
-                    one(loggingManagerMock).captureStandardOutput(LogLevel.INFO);
-                    will(returnValue(loggingManagerMock));
-                    one(loggingManagerMock).start();
-                    one(getInstallDeployTask()).execute();
-                    one(loggingManagerMock).stop();
-                    will(returnValue(loggingManagerMock));
-                }
-            }
-        });
-        getMavenResolver().commitPublishTransaction();
-        assertThat(attachedArtifact.getFile(), equalTo(classifierArtifact.getFile()));
-        assertThat(attachedArtifact.getType(), equalTo(classifierArtifact.getType()));
-        assertThat(attachedArtifact.getClassifier(), equalTo(classifierArtifact.getClassifier()));
-        assertThat(grabSettingsFileAction.getSettingsFile().exists(), equalTo(false));
-        assertThat(grabSettingsFileAction.getSettingsFileContent(), equalTo(AbstractMavenResolver.SETTINGS_XML));
-    }
-
-    private static Matcher<Pom> pomMatcher(final File expectedPomFile, final Project expectedAntProject) {
-        return new BaseMatcher<Pom>() {
-            public void describeTo(Description description) {
-                description.appendText("matching pom");
-            }
-
-            public boolean matches(Object actual) {
-                Pom actualPom = (Pom) actual;
-                return actualPom.getFile().equals(expectedPomFile) && actualPom.getProject().equals(expectedAntProject);
-            }
-        };
-    }
-
-    @Test
-    public void setFilter() {
-        final PublishFilter publishFilterMock = context.mock(PublishFilter.class);
-        context.checking(new Expectations() {{
-            one(pomFilterContainerMock).setFilter(publishFilterMock);
-        }});
-        getMavenResolver().setFilter(publishFilterMock);
-    }
-
-    @Test
-    public void getFilter() {
-        final PublishFilter publishFilterMock = context.mock(PublishFilter.class);
-        context.checking(new Expectations() {{
-            allowing(pomFilterContainerMock).getFilter();
-            will(returnValue(publishFilterMock));
-        }});
-        assertSame(publishFilterMock, getMavenResolver().getFilter());
-    }
-
-    @Test
-    public void setPom() {
-        context.checking(new Expectations() {{
-            one(pomFilterContainerMock).setPom(pomMock);
-        }});
-        getMavenResolver().setPom(pomMock);
-    }
-
-    @Test
-    public void getPom() {
-        context.checking(new Expectations() {{
-            allowing(pomFilterContainerMock).getPom();
-            will(returnValue(pomMock));
-        }});
-        assertSame(pomMock, getMavenResolver().getPom());
-    }
-
-    @Test
-    public void addFilter() {
-        final String testName = "somename";
-        final PublishFilter publishFilterMock = context.mock(PublishFilter.class);
-        context.checking(new Expectations() {{
-            one(pomFilterContainerMock).addFilter(testName, publishFilterMock);
-        }});
-        getMavenResolver().addFilter(testName, publishFilterMock);
-    }
-
-    @Test
-    public void filter() {
-        final String testName = "somename";
-        final PublishFilter publishFilterMock = context.mock(PublishFilter.class);
-        context.checking(new Expectations() {{
-            one(pomFilterContainerMock).filter(testName);
-            will(returnValue(publishFilterMock));
-        }});
-        assertSame(publishFilterMock, getMavenResolver().filter(testName));
-    }
-
-    @Test
-    public void pom() {
-        final String testName = "somename";
-        context.checking(new Expectations() {{
-            one(pomFilterContainerMock).pom(testName);
-            will(returnValue(pomMock));
-        }});
-        assertSame(pomMock, getMavenResolver().pom(testName));
-    }
-
-    private static class GrabSettingsFileAction implements org.jmock.api.Action {
-        private File settingsFile;
-        private String settingsFileContent;
-
-        public void describeTo(Description description) {
-        }
-
-        public Object invoke(Invocation invocation) throws Throwable {
-            settingsFile = (File) invocation.getParameter(0);
-            settingsFileContent = FileUtils.readFileToString(settingsFile);
-            return null;
-        }
-
-        public File getSettingsFile() {
-            return settingsFile;
-        }
-
-        public String getSettingsFileContent() {
-            return settingsFileContent;
-        }
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenDeployerTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenDeployerTest.java
deleted file mode 100644
index 7551396..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenDeployerTest.java
+++ /dev/null
@@ -1,107 +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;
-
-import org.apache.maven.artifact.ant.AttachedArtifact;
-import org.apache.maven.artifact.ant.InstallDeployTaskSupport;
-import org.apache.maven.artifact.ant.RemoteRepository;
-import org.codehaus.plexus.PlexusContainer;
-import org.codehaus.plexus.PlexusContainerException;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.PublishArtifact;
-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;
-import org.junit.runner.RunWith;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Set;
-
-import static org.junit.Assert.*;
-
-/**
- * @author Hans Dockter
- */
- at RunWith(org.jmock.integration.junit4.JMock.class)
-public class BaseMavenDeployerTest extends AbstractMavenResolverTest {
-
-    private BaseMavenDeployer mavenDeployer = createMavenDeployer();
-
-    private Factory<CustomDeployTask> deployTaskFactoryMock = context.mock(Factory.class);
-    private CustomDeployTask deployTaskMock = context.mock(CustomDeployTask.class);
-
-    private PlexusContainer plexusContainerMock = context.mock(PlexusContainer.class);
-    private RemoteRepository testRepository = new RemoteRepository();
-    private RemoteRepository testSnapshotRepository = new RemoteRepository();
-
-    private Configuration configurationStub = context.mock(Configuration.class);
-
-    protected BaseMavenDeployer createMavenDeployer() {
-        return new BaseMavenDeployer(TEST_NAME, pomFilterContainerMock, artifactPomContainerMock, loggingManagerMock);
-    }
-
-    protected MavenResolver getMavenResolver() {
-        return mavenDeployer;
-    }
-
-    protected InstallDeployTaskSupport getInstallDeployTask() {
-        return deployTaskMock;
-    }
-
-    protected PomFilterContainer createPomFilterContainerMock() {
-        return context.mock(PomFilterContainer.class);
-    }
-
-    public void setUp() {
-        super.setUp();
-        mavenDeployer = createMavenDeployer();
-        mavenDeployer.setDeployTaskFactory(deployTaskFactoryMock);
-        mavenDeployer.setRepository(testRepository);
-        mavenDeployer.setSnapshotRepository(testSnapshotRepository);
-        mavenDeployer.setConfiguration(configurationStub);
-        mavenDeployer.setUniqueVersion(false);
-    }
-
-    protected void checkTransaction(final Set<DefaultMavenDeployment> defaultMavenDeployments, AttachedArtifact attachedArtifact, PublishArtifact classifierArtifact) throws IOException, PlexusContainerException {
-        final Set<File> protocolJars = WrapUtil.toLinkedSet(new File("jar1"), new File("jar1"));
-        context.checking(new Expectations() {{
-                allowing(configurationStub).resolve();
-                will(returnValue(protocolJars));
-                allowing(deployTaskFactoryMock).create();
-                will(returnValue(getInstallDeployTask()));
-                allowing(deployTaskMock).getContainer();
-                will(returnValue(plexusContainerMock));
-                for (File protocolProviderJar : protocolJars) {
-                    one(plexusContainerMock).addJarResource(protocolProviderJar);
-                }
-                one(deployTaskMock).setUniqueVersion(mavenDeployer.isUniqueVersion());
-                one(deployTaskMock).addRemoteRepository(testRepository);
-                one(deployTaskMock).addRemoteSnapshotRepository(testSnapshotRepository);
-        }});
-        super.checkTransaction(defaultMavenDeployments, attachedArtifact, classifierArtifact);
-    }
-
-    @Test
-    public void init() {
-        mavenDeployer = new BaseMavenDeployer(TEST_NAME, pomFilterContainerMock, artifactPomContainerMock, loggingManagerMock);
-        assertTrue(mavenDeployer.isUniqueVersion());
-    }
-
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenInstallerTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenInstallerTest.java
deleted file mode 100644
index 8a347d2..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenInstallerTest.java
+++ /dev/null
@@ -1,72 +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;
-
-import org.apache.maven.artifact.ant.AttachedArtifact;
-import org.apache.maven.artifact.ant.InstallDeployTaskSupport;
-import org.codehaus.plexus.PlexusContainerException;
-import org.gradle.api.artifacts.PublishArtifact;
-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;
-import java.util.Set;
-
-/**
- * @author Hans Dockter
- */
-public class BaseMavenInstallerTest extends AbstractMavenResolverTest {
-    private BaseMavenInstaller mavenInstaller;
-
-    private Factory<CustomInstallTask> installTaskFactoryMock;
-    private CustomInstallTask installTaskMock;
-
-    protected BaseMavenInstaller createMavenInstaller() {
-        return new BaseMavenInstaller(TEST_NAME, pomFilterContainerMock, artifactPomContainerMock, loggingManagerMock);
-    }
-
-    protected PomFilterContainer createPomFilterContainerMock() {
-        return context.mock(PomFilterContainer.class);
-    }
-
-    protected MavenResolver getMavenResolver() {
-        return mavenInstaller;
-    }
-
-    protected InstallDeployTaskSupport getInstallDeployTask() {
-        return installTaskMock;
-    }
-
-    public void setUp() {
-        super.setUp();
-        installTaskFactoryMock = context.mock(Factory.class);
-        installTaskMock = context.mock(CustomInstallTask.class);
-        mavenInstaller = createMavenInstaller();
-        mavenInstaller.setInstallTaskFactory(installTaskFactoryMock);
-    }
-
-    protected void checkTransaction(final Set<DefaultMavenDeployment> deployableUnits, AttachedArtifact attachedArtifact, PublishArtifact classifierArtifact) throws IOException, PlexusContainerException {
-        context.checking(new Expectations() {
-            {
-                allowing(installTaskFactoryMock).create();
-                will(returnValue(getInstallDeployTask()));
-            }
-        });
-        super.checkTransaction(deployableUnits, attachedArtifact, classifierArtifact);
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultArtifactPomContainerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultArtifactPomContainerTest.groovy
deleted file mode 100644
index c957ace..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultArtifactPomContainerTest.groovy
+++ /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.internal.artifacts.publish.maven.deploy
-
-import spock.lang.Specification
-import org.gradle.api.internal.artifacts.publish.maven.MavenPomMetaInfoProvider
-import org.gradle.api.artifacts.maven.PomFilterContainer
-import org.apache.ivy.core.module.descriptor.Artifact
-import org.gradle.api.artifacts.maven.PublishFilter
-import org.gradle.api.artifacts.maven.MavenPom
-import org.gradle.api.artifacts.PublishArtifact
-
-class DefaultArtifactPomContainerTest extends Specification {
-    final MavenPomMetaInfoProvider pomMetaInfoProvider = Mock()
-    final PomFilterContainer pomFilterContainer = Mock()
-    final ArtifactPomFactory artifactPomFactory = Mock()
-    final File pomDir = new File('pomDir')
-    final DefaultArtifactPomContainer container = new DefaultArtifactPomContainer(pomMetaInfoProvider, pomFilterContainer, artifactPomFactory)
-
-    def setup() {
-        _ * pomMetaInfoProvider.mavenPomDir >> pomDir
-    }
-    
-    def addsArtifactToFirstMatchingArtifactPom() {
-        File artifactFile = new File('artifact')
-        Artifact artifact = artifact()
-        MavenPom templatePom = Mock()
-        PomFilter filter1 = alwaysFilter('filterName', templatePom)
-        PomFilter filter2 = neverFilter()
-        ArtifactPom pom = pom('artifactId')
-        PublishArtifact pomArtifact = Mock()
-        PublishArtifact mainArtifact = Mock()
-        PublishArtifact attachArtifact = Mock()
-
-        when:
-        container.addArtifact(artifact, artifactFile)
-        def infos = container.createDeployableFilesInfos()
-
-        then:
-        _ * pomFilterContainer.activePomFilters >> [filter1, filter2]
-        _ * artifactPomFactory.createArtifactPom(templatePom) >> pom
-        _ * pom.writePom(new File(pomDir, 'pom-filterName.xml')) >> pomArtifact
-        _ * pom.artifact >> mainArtifact
-        _ * pom.attachedArtifacts >> ([attachArtifact] as Set)
-
-        infos.size() == 1
-        DefaultMavenDeployment info = infos.asList()[0]
-
-        info.pomArtifact == pomArtifact
-        info.mainArtifact == mainArtifact
-        info.artifacts == [pomArtifact, mainArtifact, attachArtifact] as Set
-        info.attachedArtifacts == [attachArtifact] as Set
-    }
-
-    def alwaysFilter(String filterName, MavenPom template) {
-        filter(filterName, true, template)
-    }
-
-    def neverFilter() {
-        filter('never', false)
-    }
-
-    def filter(String name, boolean accept, MavenPom template = null) {
-        PomFilter filter = Mock()
-        PublishFilter publishFilter = Mock()
-        _ * filter.name >> name
-        _ * filter.filter >> publishFilter
-        _ * publishFilter.accept(_, _) >> accept
-        _ * filter.pomTemplate >> template
-        filter
-    }
-
-    def artifact() {
-        Artifact artifact = Mock()
-        return artifact
-    }
-
-    def pom(String artifactId) {
-        ArtifactPom pom = Mock()
-        MavenPom mavenPom = Mock()
-        _ * pom.pom >> mavenPom
-        _ * mavenPom.artifactId >> artifactId
-        return pom
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultArtifactPomTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultArtifactPomTest.java
deleted file mode 100644
index 9a0f068..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultArtifactPomTest.java
+++ /dev/null
@@ -1,272 +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.artifacts.publish.maven.deploy;
-
-import org.apache.ivy.core.module.descriptor.Artifact;
-import org.apache.ivy.core.module.descriptor.DefaultArtifact;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.artifacts.ConfigurationContainer;
-import org.gradle.api.artifacts.Dependency;
-import org.gradle.api.artifacts.PublishArtifact;
-import org.gradle.api.artifacts.maven.MavenPom;
-import org.gradle.api.internal.artifacts.publish.maven.DefaultMavenPom;
-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;
-import org.gradle.util.GUtil;
-import org.gradle.util.TemporaryFolder;
-import org.jmock.Expectations;
-import org.jmock.Mockery;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-/**
- * @author Hans Dockter
- */
-public class DefaultArtifactPomTest {
-    private DefaultArtifactPom artifactPom;
-    private MavenPom testPom;
-
-    @Rule
-    public TemporaryFolder tmpDir = new TemporaryFolder();
-
-    Mockery context = new JUnit4Mockery();
-
-    @Before
-    public void setUp() {
-        testPom = new DefaultMavenPom(context.mock(ConfigurationContainer.class), new DefaultConf2ScopeMappingContainer(),
-                context.mock(PomDependenciesConverter.class), context.mock(FileResolver.class));
-        artifactPom = new DefaultArtifactPom(testPom);
-    }
-
-    @Test
-    public void pomWithMainArtifact() {
-        Artifact mainArtifact = createTestArtifact("someName", null, "mainPackaging");
-        File mainFile = new File("someFile");
-
-        artifactPom.addArtifact(mainArtifact, mainFile);
-
-        assertThat(artifactPom.getArtifact().getName(), equalTo("someName"));
-        assertThat(artifactPom.getArtifact().getExtension(), equalTo("mainPackaging"));
-        assertThat(artifactPom.getArtifact().getType(), equalTo("mainPackaging"));
-        assertThat(artifactPom.getArtifact().getClassifier(), nullValue());
-        assertThat(artifactPom.getArtifact().getFile(), equalTo(mainFile));
-
-        assertThat(artifactPom.getPom().getGroupId(), equalTo("org"));
-        assertThat(artifactPom.getPom().getArtifactId(), equalTo("someName"));
-        assertThat(artifactPom.getPom().getVersion(), equalTo("1.0"));
-        assertThat(artifactPom.getPom().getPackaging(), equalTo("mainPackaging"));
-    }
-
-    @Test
-    public void pomWithMainArtifactAndClassifierArtifacts() {
-        Artifact mainArtifact = createTestArtifact("someName", null, "mainPackaging");
-        File mainFile = new File("someFile");
-        Artifact classifierArtifact = createTestArtifact("otherName", "javadoc", "zip");
-        File classifierFile = new File("someClassifierFile");
-
-        artifactPom.addArtifact(mainArtifact, mainFile);
-        artifactPom.addArtifact(classifierArtifact, classifierFile);
-
-        assertThat(artifactPom.getArtifact().getName(), equalTo("someName"));
-        assertThat(artifactPom.getArtifact().getExtension(), equalTo("mainPackaging"));
-        assertThat(artifactPom.getArtifact().getType(), equalTo("mainPackaging"));
-        assertThat(artifactPom.getArtifact().getClassifier(), nullValue());
-        assertThat(artifactPom.getArtifact().getFile(), equalTo(mainFile));
-
-        PublishArtifact artifact = singleItem(artifactPom.getAttachedArtifacts());
-        assertThat(artifact.getName(), equalTo("someName"));
-        assertThat(artifact.getExtension(), equalTo("zip"));
-        assertThat(artifact.getType(), equalTo("zip"));
-        assertThat(artifact.getClassifier(), equalTo("javadoc"));
-        assertThat(artifact.getFile(), equalTo(classifierFile));
-
-        assertThat(artifactPom.getPom().getGroupId(), equalTo("org"));
-        assertThat(artifactPom.getPom().getArtifactId(), equalTo("someName"));
-        assertThat(artifactPom.getPom().getVersion(), equalTo("1.0"));
-        assertThat(artifactPom.getPom().getPackaging(), equalTo("mainPackaging"));
-    }
-
-    @Test
-    public void pomWithClassifierArtifactsOnly() {
-        File classifierFile = new File("someClassifierFile");
-        Artifact classifierArtifact = createTestArtifact("someName", "javadoc", "zip");
-
-        artifactPom.addArtifact(classifierArtifact, classifierFile);
-
-        assertThat(artifactPom.getArtifact(), nullValue());
-
-        PublishArtifact artifact = singleItem(artifactPom.getAttachedArtifacts());
-        assertThat(artifact.getName(), equalTo("someName"));
-        assertThat(artifact.getExtension(), equalTo("zip"));
-        assertThat(artifact.getType(), equalTo("zip"));
-        assertThat(artifact.getClassifier(), equalTo("javadoc"));
-        assertThat(artifact.getFile(), equalTo(classifierFile));
-
-        assertThat(artifactPom.getPom().getGroupId(), equalTo("org"));
-        assertThat(artifactPom.getPom().getArtifactId(), equalTo("someName"));
-        assertThat(artifactPom.getPom().getVersion(), equalTo("1.0"));
-        assertThat(artifactPom.getPom().getPackaging(), equalTo("jar"));
-    }
-
-    @Test
-    public void pomWithMainArtifactAndMetadataArtifacts() {
-        Artifact mainArtifact = createTestArtifact("someName", null, "mainPackaging");
-        File mainFile = new File("someFile");
-        File metadataFile = new File("someMetadataFile");
-        Artifact metadataArtifact = createTestArtifact("otherName", null, "sometype");
-
-        artifactPom.addArtifact(mainArtifact, mainFile);
-        artifactPom.addArtifact(metadataArtifact, metadataFile);
-
-        assertThat(artifactPom.getArtifact().getName(), equalTo("someName"));
-        assertThat(artifactPom.getArtifact().getExtension(), equalTo("mainPackaging"));
-        assertThat(artifactPom.getArtifact().getType(), equalTo("mainPackaging"));
-        assertThat(artifactPom.getArtifact().getClassifier(), nullValue());
-        assertThat(artifactPom.getArtifact().getFile(), equalTo(mainFile));
-
-        PublishArtifact artifact = singleItem(artifactPom.getAttachedArtifacts());
-        assertThat(artifact.getName(), equalTo("someName"));
-        assertThat(artifact.getExtension(), equalTo("sometype"));
-        assertThat(artifact.getType(), equalTo("sometype"));
-        assertThat(artifact.getClassifier(), nullValue());
-        assertThat(artifact.getFile(), equalTo(metadataFile));
-
-        assertThat(artifactPom.getPom().getGroupId(), equalTo("org"));
-        assertThat(artifactPom.getPom().getArtifactId(), equalTo("someName"));
-        assertThat(artifactPom.getPom().getVersion(), equalTo("1.0"));
-        assertThat(artifactPom.getPom().getPackaging(), equalTo("mainPackaging"));
-    }
-    
-    @Test(expected = InvalidUserDataException.class)
-    public void addClassifierTwiceShouldThrowInvalidUserDataEx() {
-        File classifierFile = new File("someClassifierFile");
-        Artifact classifierArtifact = createTestArtifact("someName", "javadoc");
-        artifactPom.addArtifact(classifierArtifact, classifierFile);
-        artifactPom.addArtifact(classifierArtifact, classifierFile);
-    }
-
-    @Test(expected = InvalidUserDataException.class)
-    public void addMainArtifactTwiceShouldThrowInvalidUserDataEx() {
-        Artifact mainArtifact = createTestArtifact("someName", null, "mainPackaging");
-        File mainFile = new File("someFile");
-        artifactPom.addArtifact(mainArtifact, mainFile);
-        artifactPom.addArtifact(mainArtifact, mainFile);
-    }
-
-    @Test
-    public void cannotAddMultipleArtifactsWithTheSameTypeAndClassifier() {
-
-        // No classifier
-        Artifact mainArtifact = createTestArtifact("someName", null);
-        artifactPom.addArtifact(mainArtifact, new File("someFile"));
-
-        assertIsDuplicate(mainArtifact, new File("someFile"));
-        assertIsDuplicate(mainArtifact, new File("otherFile"));
-        assertIsDuplicate(createTestArtifact("otherName", null), new File("otherFile"));
-
-        // Classifier
-        Artifact classifierArtifact = createTestArtifact("someName", "classifier");
-        artifactPom.addArtifact(classifierArtifact, new File("classifierFile"));
-
-        assertIsDuplicate(classifierArtifact, new File("someFile"));
-        assertIsDuplicate(classifierArtifact, new File("otherFile"));
-        assertIsDuplicate(createTestArtifact("otherName", "classifier"), new File("otherFile"));
-    }
-
-    private void assertIsDuplicate(Artifact artifact, File file) {
-        try {
-            artifactPom.addArtifact(artifact, file);
-            fail();
-        } catch (InvalidUserDataException e) {
-            assertThat(e.getMessage(), startsWith("A POM cannot have multiple artifacts with the same type and classifier."));
-        }
-    }
-
-    @Test
-    public void initWithCustomPomSettings() {
-        Artifact mainArtifact = createTestArtifact("someName", null, "mainPackaging");
-        File mainFile = new File("someFile");
-
-        testPom.setArtifactId("customArtifactId");
-        testPom.setGroupId("customGroupId");
-        testPom.setVersion("customVersion");
-        testPom.setPackaging("customPackaging");
-
-        artifactPom.addArtifact(mainArtifact, mainFile);
-
-        assertThat(artifactPom.getArtifact().getName(), equalTo("customArtifactId"));
-        assertThat(artifactPom.getArtifact().getExtension(), equalTo("mainPackaging"));
-        assertThat(artifactPom.getArtifact().getType(), equalTo("mainPackaging"));
-        assertThat(artifactPom.getArtifact().getClassifier(), nullValue());
-        assertThat(artifactPom.getArtifact().getFile(), equalTo(mainFile));
-
-        assertThat(artifactPom.getPom().getGroupId(), equalTo("customGroupId"));
-        assertThat(artifactPom.getPom().getArtifactId(), equalTo("customArtifactId"));
-        assertThat(artifactPom.getPom().getVersion(), equalTo("customVersion"));
-        assertThat(artifactPom.getPom().getPackaging(), equalTo("mainPackaging"));
-    }
-
-    private Artifact createTestArtifact(String name, String classifier) {
-        return createTestArtifact(name, classifier, "jar");
-    }
-
-    private Artifact createTestArtifact(String name, String classifier, String type) {
-        Map<String, String> extraAttributes = new HashMap<String, String>();
-        if (classifier != null) {
-            extraAttributes.put(Dependency.CLASSIFIER, classifier);
-        }
-        return new DefaultArtifact(ModuleRevisionId.newInstance("org", name, "1.0"), null, name, type, type, extraAttributes);
-    }
-
-    @Test
-    public void writePom() {
-        final MavenPom mavenPomMock = context.mock(MavenPom.class);
-        DefaultArtifactPom artifactPom = new DefaultArtifactPom(mavenPomMock);
-        final File somePomFile = new File(tmpDir.getDir(), "someDir/somePath");
-        context.checking(new Expectations() {{
-            allowing(mavenPomMock).getArtifactId();
-            will(returnValue("artifactId"));
-            one(mavenPomMock).writeTo(with(any(FileOutputStream.class)));
-        }});
-
-        PublishArtifact artifact = artifactPom.writePom(somePomFile);
-
-        assertThat(artifact.getName(), equalTo("artifactId"));
-        assertThat(artifact.getType(), equalTo("pom"));
-        assertThat(artifact.getExtension(), equalTo("pom"));
-        assertThat(artifact.getClassifier(), nullValue());
-        assertThat(artifact.getFile(), equalTo(somePomFile));
-    }
-
-    private <T> T singleItem(Iterable<? extends T> collection) {
-        List<T> elements = GUtil.addLists(collection);
-        assertThat(elements.size(), equalTo(1));
-        return elements.get(0);
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenDeployerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenDeployerTest.groovy
deleted file mode 100644
index 0ebc7ce..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenDeployerTest.groovy
+++ /dev/null
@@ -1,122 +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 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
-
-/**
- * @author Hans Dockter
- */
- at RunWith(JMock)
-class DefaultGroovyPomFilterContainerTest extends BasePomFilterContainerTest {
-    static final String TEST_NAME = "somename"
-    PomFilterContainer groovyPomFilterContainer
-
-    @Before
-    public void setUp() {
-        super.setUp()
-    }
-
-    protected BasePomFilterContainer createPomFilterContainer() {
-        return groovyPomFilterContainer = new BasePomFilterContainer(mavenPomFactoryMock);
-    }
-
-    @Test
-    public void addFilterWithClosure() {
-        Closure closureFilter = {}
-        MavenPom pom = groovyPomFilterContainer.addFilter(TEST_NAME, closureFilter)
-        assertSame(pomMock, pom);
-        assertSame(pomMock, groovyPomFilterContainer.pom(TEST_NAME));
-        assertSame(closureFilter, getClosureFromProxy(groovyPomFilterContainer.filter(TEST_NAME)));
-    }
-
-    private Closure getClosureFromProxy(PublishFilter filter) {
-        Proxy.getInvocationHandler(filter).delegate
-    }
-
-    @Test
-    public void filterWithClosure() {
-        Closure closureFilter = {}
-        context.checking {
-            one(pomFilterMock).setFilter(withParam(FilterMatcher.equalsFilter(closureFilter)))
-        }
-        groovyPomFilterContainer.filter(closureFilter)
-    }
-
-    @Test
-    public void defaultPomWithClosure() {
-        String testGroup = "testGroup"
-        context.checking {
-            one(pomFilterMock).getPomTemplate(); will(returnValue(pomMock))
-            one(pomMock).setGroupId(testGroup);
-        }
-        groovyPomFilterContainer.pom {
-            groupId = testGroup
-        }
-    }
-
-    @Test
-    public void pomWithClosure() {
-        groovyPomFilterContainer.addFilter(TEST_NAME, {})
-        String testGroup = "testGroup"
-        context.checking {
-            one(pomMock).setGroupId(testGroup);
-        }
-        groovyPomFilterContainer.pom(TEST_NAME) {
-            groupId = testGroup
-        }
-    }
-}
-
-public class FilterMatcher extends BaseMatcher {
-    Closure filter
-
-    public void describeTo(Description description) {
-        description.appendText("matching filter");
-    }
-
-    public boolean matches(Object actual) {
-        return getClosureFromProxy(actual) == filter;
-    }
-
-    private Closure getClosureFromProxy(PublishFilter filter) {
-        Proxy.getInvocationHandler(filter).delegate
-    }
-
-
-    @Factory
-    public static Matcher<PublishFilter> equalsFilter(Closure filter) {
-        return new FilterMatcher(filter: filter);
-    }
-
-}
-
-
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenUploaderTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenUploaderTest.groovy
deleted file mode 100644
index 5172ebd..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenUploaderTest.groovy
+++ /dev/null
@@ -1,118 +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.PomFilterContainer
-import org.gradle.api.internal.artifacts.publish.maven.deploy.BaseMavenDeployer
-import org.gradle.api.internal.artifacts.publish.maven.deploy.BaseMavenDeployerTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import static org.junit.Assert.assertEquals
-
-/**
- * @author Hans Dockter
- */
- at RunWith (org.jmock.integration.junit4.JMock.class)
-class DefaultGroovyMavenDeployerTest extends BaseMavenDeployerTest {
-    private DefaultGroovyMavenDeployer groovyMavenDeployer;
-    private DefaultGroovyPomFilterContainerTest groovyMavenResolverHelper
-
-    protected PomFilterContainer createPomFilterContainerMock() {
-        context.mock(PomFilterContainer.class);
-    }
-
-    protected BaseMavenDeployer createMavenDeployer() {
-        groovyMavenDeployer = new DefaultGroovyMavenDeployer(TEST_NAME, pomFilterContainerMock, artifactPomContainerMock, loggingManagerMock)
-    }
-
-    @Before
-    void setUp() {
-        super.setUp();
-    }
-
-    @Test
-    void repositoryBuilder() {
-        checkRepositoryBuilder(DefaultGroovyMavenDeployer.REPOSITORY_BUILDER)
-    }
-
-    @Test
-    void snapshotRepositoryBuilder() {
-        checkRepositoryBuilder(DefaultGroovyMavenDeployer.SNAPSHOT_REPOSITORY_BUILDER)
-    }
-
-
-    void checkRepositoryBuilder(String repositoryName) {
-        String testUrl = 'testUrl'
-        String testProxyHost = 'hans'
-        String testUserName = 'userId'
-        String testSnapshotUpdatePolicy = 'always'
-        String testReleaseUpdatePolicy = 'never'
-        groovyMavenDeployer."$repositoryName"(url: testUrl) {
-            authentication(userName: testUserName)
-            proxy(host: testProxyHost)
-            releases(updatePolicy: testReleaseUpdatePolicy)
-            snapshots(updatePolicy: testSnapshotUpdatePolicy)
-        }
-        assertEquals(testUrl, groovyMavenDeployer."$repositoryName".url)
-        assertEquals(testUserName, groovyMavenDeployer."$repositoryName".authentication.userName)
-        assertEquals(testProxyHost, groovyMavenDeployer."$repositoryName".proxy.host)
-        assertEquals(testReleaseUpdatePolicy, groovyMavenDeployer."$repositoryName".releases.updatePolicy)
-        assertEquals(testSnapshotUpdatePolicy, groovyMavenDeployer."$repositoryName".snapshots.updatePolicy)
-    }
-
-    @Test
-    void filter() {
-        Closure testClosure = {}
-        context.checking {
-            one(pomFilterContainerMock).filter(testClosure)
-        }
-        groovyMavenDeployer.filter(testClosure)
-    }
-
-    @Test
-    void pom() {
-        Closure testClosure = {}
-        context.checking {
-            one(pomFilterContainerMock).pom(testClosure)
-        }
-        groovyMavenDeployer.pom(testClosure)
-    }
-
-    @Test
-    void pomWithName() {
-        Closure testClosure = {}
-        String testName = 'somename'
-        context.checking {
-            one(pomFilterContainerMock).pom(testName, testClosure)
-        }
-        groovyMavenDeployer.pom(testName, testClosure)
-    }
-
-    @Test
-    void addFilter() {
-        Closure testClosure = {}
-        String testName = 'somename'
-        context.checking {
-            one(pomFilterContainerMock).addFilter(testName, testClosure)
-        }
-        groovyMavenDeployer.addFilter(testName, testClosure)
-    }
-}
-
-
-
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/CacheBackedFileSnapshotRepositoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/CacheBackedFileSnapshotRepositoryTest.groovy
new file mode 100644
index 0000000..6b17dc8
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/CacheBackedFileSnapshotRepositoryTest.groovy
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection
+
+import spock.lang.Specification
+import org.gradle.cache.CacheRepository
+import org.gradle.cache.CacheBuilder
+import org.gradle.cache.PersistentCache
+import org.gradle.cache.PersistentIndexedCache
+
+class CacheBackedFileSnapshotRepositoryTest extends Specification {
+    final CacheRepository cacheRepository = Mock()
+    final PersistentIndexedCache<Object, Object> indexedCache = Mock()
+    final FileSnapshotRepository repository = new CacheBackedFileSnapshotRepository(cacheRepository)
+
+    def "assigns an id when a snapshot is added"() {
+        FileCollectionSnapshot snapshot = Mock()
+
+        when:
+        def id = repository.add(snapshot)
+
+        then:
+        id == 4
+        interaction {
+            expectCacheOpened()
+        }
+        1 * indexedCache.get("nextId") >> (4 as Long)
+        1 * indexedCache.put("nextId", 5)
+        1 * indexedCache.put(4, snapshot)
+        0 * _._
+    }
+
+    def "can fetch a snapshot by id"() {
+        FileCollectionSnapshot snapshot = Mock()
+
+        when:
+        def result = repository.get(4)
+
+        then:
+        result == snapshot
+        interaction {
+            expectCacheOpened()
+        }
+        1 * indexedCache.get(4) >> snapshot
+        0 * _._
+    }
+
+    def "can delete a snapshot by id"() {
+        when:
+        repository.remove(4)
+
+        then:
+        interaction {
+            expectCacheOpened()
+        }
+        1 * indexedCache.remove(4)
+        0 * _._
+    }
+
+    def expectCacheOpened() {
+        CacheBuilder builder = Mock()
+        PersistentCache cache = Mock()
+        1 * cacheRepository.cache("fileSnapshots") >> builder
+        1 * builder.open() >> cache
+        1 * cache.openIndexedCache() >> indexedCache
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/CompositeUpToDateRuleTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/CompositeUpToDateRuleTest.groovy
new file mode 100644
index 0000000..ef36edf
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/CompositeUpToDateRuleTest.groovy
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection
+
+import spock.lang.Specification
+import org.gradle.api.internal.TaskInternal
+
+class CompositeUpToDateRuleTest extends Specification {
+    final UpToDateRule rule1 = Mock()
+    final UpToDateRule rule2 = Mock()
+    final UpToDateRule.TaskUpToDateState state1 = Mock()
+    final UpToDateRule.TaskUpToDateState state2 = Mock()
+    final TaskInternal task = Mock()
+    final TaskExecution previous = Mock()
+    final TaskExecution current = Mock()
+    final CompositeUpToDateRule rule = new CompositeUpToDateRule(rule1, rule2)
+
+    def delegatesToEachRuleInOrder() {
+        when:
+        def state = rule.create(task, previous, current)
+
+        then:
+        1 * rule1.create(task, previous, current) >> state1
+        1 * rule2.create(task, previous, current) >> state2
+
+        when:
+        state.checkUpToDate([])
+
+        then:
+        1 * state1.checkUpToDate([])
+        1 * state2.checkUpToDate([])
+
+        when:
+        state.snapshotAfterTask()
+
+        then:
+        1 * state1.snapshotAfterTask()
+        1 * state2.snapshotAfterTask()
+    }
+
+    def checkUpToDateStopsAtFirstRuleWhichMarksTaskOutOfDate() {
+        when:
+        def state = rule.create(task, previous, current)
+
+        then:
+        1 * rule1.create(task, previous, current) >> state1
+        1 * rule2.create(task, previous, current) >> state2
+
+        when:
+        state.checkUpToDate([])
+
+        then:
+        1 * state1.checkUpToDate([]) >> { args -> args[0] << 'out-of-date' }
+        0 * state2.checkUpToDate(_)
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/DefaultFileSnapshotterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/DefaultFileSnapshotterTest.groovy
index 9af10d8..5bc634d 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/DefaultFileSnapshotterTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/DefaultFileSnapshotterTest.groovy
@@ -1,330 +1,330 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection
-
-
-import static org.junit.Assert.*
-import static org.hamcrest.Matchers.*
-
-import org.gradle.util.JUnit4GroovyMockery
-import org.jmock.integration.junit4.JMock
-import org.junit.runner.RunWith
-import org.junit.Test
-import org.junit.Rule
-import org.gradle.util.TemporaryFolder
-import org.gradle.util.TestFile
-import org.gradle.api.file.FileCollection
-import org.gradle.util.ChangeListener
-import org.gradle.api.file.FileTree
-
- at RunWith(JMock.class)
-public class DefaultFileSnapshotterTest {
-    private final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
-    private final Hasher hasher = new DefaultHasher()
-    private int counter
-    private ChangeListener listener = context.mock(ChangeListener.class)
-    private final DefaultFileSnapshotter snapshotter = new DefaultFileSnapshotter(hasher)
-    @Rule
-    public final TemporaryFolder tmpDir = new TemporaryFolder()
-
-    @Test
-    public void getFilesReturnsOnlyTheFilesWhichExisted() {
-        TestFile file = tmpDir.createFile('file1')
-        TestFile dir = tmpDir.createDir('file2')
-        TestFile noExist = tmpDir.file('file3')
-
-        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file, dir, noExist))
-
-        assertThat(snapshot.files.files as List, equalTo([file]))
-    }
-    
-    @Test
-    public void notifiesListenerWhenFileAdded() {
-        TestFile file1 = tmpDir.createFile('file1')
-        TestFile file2 = tmpDir.createFile('file2')
-
-        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file1))
-
-        context.checking {
-            one(listener).added(file2)
-        }
-        snapshotter.snapshot(files(file1, file2)).changesSince(snapshot, listener)
-    }
-
-    @Test
-    public void notifiesListenerWhenFileRemoved() {
-        TestFile file1 = tmpDir.createFile('file1')
-        TestFile file2 = tmpDir.createFile('file2')
-
-        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file1, file2))
-
-        context.checking {
-            one(listener).removed(file2)
-        }
-        snapshotter.snapshot(files(file1)).changesSince(snapshot, listener)
-    }
-
-    @Test
-    public void fileHasNotChangedWhenTypeAndHashHaveNotChanged() {
-        TestFile file = tmpDir.createFile('file')
-
-        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file))
-        assertThat(snapshot, notNullValue())
-
-        snapshotter.snapshot(files(file)).changesSince(snapshot, listener)
-    }
-
-    @Test
-    public void fileHasChangedWhenTypeHasChanged() {
-        TestFile file = tmpDir.createFile('file')
-
-        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file))
-
-        file.delete()
-        file.createDir()
-
-        context.checking {
-            one(listener).changed(file)
-        }
-        snapshotter.snapshot(files(file)).changesSince(snapshot, listener)
-    }
-
-    @Test
-    public void fileHasChangedWhenHashHasChanged() {
-        TestFile file = tmpDir.createFile('file')
-
-        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file))
-
-        file.write('new content')
-
-        context.checking {
-            one(listener).changed(file)
-        }
-        snapshotter.snapshot(files(file)).changesSince(snapshot, listener)
-    }
-
-    @Test
-    public void directoryHasNotChangedWhenTypeHasNotChanged() {
-        TestFile dir = tmpDir.createDir('dir')
-
-        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(dir))
-
-        snapshotter.snapshot(files(dir)).changesSince(snapshot, listener)
-    }
-
-    @Test
-    public void directoryHasChangedWhenTypeHasChanged() {
-        TestFile dir = tmpDir.createDir('dir')
-
-        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(dir))
-
-        dir.deleteDir()
-        dir.createFile()
-
-        context.checking {
-            one(listener).changed(dir)
-        }
-        snapshotter.snapshot(files(dir)).changesSince(snapshot, listener)
-    }
-
-    @Test
-    public void nonExistentFileUnchangedWhenTypeHasNotChanged() {
-        TestFile file = tmpDir.file('unknown')
-
-        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file))
-
-        snapshotter.snapshot(files(file)).changesSince(snapshot, listener)
-    }
-
-    @Test
-    public void nonExistentFileIsChangedWhenTypeHasChanged() {
-        TestFile file = tmpDir.file('unknown')
-
-        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file))
-
-        file.createFile()
-
-        context.checking {
-            one(listener).changed(file)
-        }
-        snapshotter.snapshot(files(file)).changesSince(snapshot, listener)
-    }
-
-    @Test
-    public void ignoresDuplicatesInFileCollection() {
-        TestFile file1 = tmpDir.createFile('file')
-        TestFile file2 = tmpDir.createFile('file')
-
-        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file1, file2))
-
-        snapshotter.snapshot(files(file1)).changesSince(snapshot, listener)
-    }
-
-    @Test
-    public void canCreateEmptySnapshot() {
-        TestFile file = tmpDir.createFile('file')
-        FileCollectionSnapshot snapshot = snapshotter.snapshot()
-
-        FileCollectionSnapshot newSnapshot = snapshotter.snapshot(files(file))
-
-        context.checking {
-            one(listener).added(file)
-        }
-
-        newSnapshot.changesSince(snapshot, listener)
-    }
-
-    @Test
-    public void diffAddsAddedFilesToSnapshot() {
-        TestFile file = tmpDir.createFile('file')
-        ChangeListener<FileCollectionSnapshot.Merge> mergeListener = context.mock(ChangeListener.class)
-
-        FileCollectionSnapshot original = snapshotter.snapshot()
-        FileCollectionSnapshot modified = snapshotter.snapshot(files(file))
-
-        context.checking {
-            one(mergeListener).added(withParam(notNullValue()))
-        }
-
-        FileCollectionSnapshot target = modified.changesSince(original).applyTo(snapshotter.snapshot(), mergeListener)
-
-        context.checking {
-            one(listener).added(file)
-        }
-        target.changesSince(original, listener)
-    }
-
-    @Test
-    public void canIgnoreAddedFileInDiff() {
-        TestFile file = tmpDir.createFile('file')
-        ChangeListener<FileCollectionSnapshot.Merge> mergeListener = context.mock(ChangeListener.class)
-
-        FileCollectionSnapshot original = snapshotter.snapshot()
-        FileCollectionSnapshot modified = snapshotter.snapshot(files(file))
-
-        context.checking {
-            one(mergeListener).added(withParam(notNullValue()))
-            will {merge -> merge.ignore()}
-        }
-
-        FileCollectionSnapshot target = modified.changesSince(original).applyTo(snapshotter.snapshot(), mergeListener)
-
-        target.changesSince(original, listener)
-    }
-
-    @Test
-    public void diffAddsChangedFilesToSnapshot() {
-        TestFile file = tmpDir.createFile('file')
-        ChangeListener<FileCollectionSnapshot.Merge> mergeListener = context.mock(ChangeListener.class)
-
-        FileCollectionSnapshot original = snapshotter.snapshot(files(file))
-        file.write('new content')
-        FileCollectionSnapshot modified = snapshotter.snapshot(files(file))
-
-        context.checking {
-            one(mergeListener).changed(withParam(notNullValue()))
-        }
-
-        FileCollectionSnapshot target = modified.changesSince(original).applyTo(snapshotter.snapshot(), mergeListener)
-
-        context.checking {
-            one(listener).changed(file)
-        }
-        target.changesSince(original, listener)
-    }
-
-    @Test
-    public void canIgnoreChangedFileInDiff() {
-        TestFile file = tmpDir.createFile('file')
-        ChangeListener<FileCollectionSnapshot.Merge> mergeListener = context.mock(ChangeListener.class)
-
-        FileCollectionSnapshot original = snapshotter.snapshot(files(file))
-        FileCollectionSnapshot target = snapshotter.snapshot(files(file))
-        file.write('new content')
-        FileCollectionSnapshot modified = snapshotter.snapshot(files(file))
-
-        context.checking {
-            one(mergeListener).changed(withParam(notNullValue()))
-            will {merge -> merge.ignore()}
-        }
-
-        target = modified.changesSince(original).applyTo(target, mergeListener)
-
-        target.changesSince(original, listener)
-    }
-
-    @Test
-    public void diffRemovesDeletedFilesFromSnapshot() {
-        TestFile file = tmpDir.createFile('file')
-        ChangeListener<FileCollectionSnapshot.Merge> mergeListener = context.mock(ChangeListener.class)
-
-        FileCollectionSnapshot original = snapshotter.snapshot(files(file))
-        FileCollectionSnapshot modified = snapshotter.snapshot()
-
-        context.checking {
-            one(mergeListener).removed(withParam(notNullValue()))
-        }
-
-        FileCollectionSnapshot target = modified.changesSince(original).applyTo(snapshotter.snapshot(files(file)), mergeListener)
-
-        context.checking {
-            one(listener).removed(file)
-        }
-        target.changesSince(original, listener)
-    }
-
-    @Test
-    public void canIgnoreRemovedFileInDiff() {
-        TestFile file = tmpDir.createFile('file')
-        ChangeListener<FileCollectionSnapshot.Merge> mergeListener = context.mock(ChangeListener.class)
-
-        FileCollectionSnapshot original = snapshotter.snapshot(files(file))
-        FileCollectionSnapshot modified = snapshotter.snapshot()
-
-        context.checking {
-            one(mergeListener).removed(withParam(notNullValue()))
-            will{merge -> merge.ignore()}
-        }
-
-        FileCollectionSnapshot target = modified.changesSince(original).applyTo(snapshotter.snapshot(files(file)), mergeListener)
-
-        target.changesSince(original, listener)
-    }
-
-    @Test
-    public void diffIgnoresUnchangedFilesInSnapshot() {
-        TestFile file = tmpDir.createFile('file')
-        ChangeListener<FileCollectionSnapshot.Merge> mergeListener = context.mock(ChangeListener.class)
-
-        FileCollectionSnapshot original = snapshotter.snapshot(files(file))
-        FileCollectionSnapshot modified = snapshotter.snapshot(files(file))
-        FileCollectionSnapshot target = modified.changesSince(original).applyTo(snapshotter.snapshot(), mergeListener)
-
-        target.changesSince(snapshotter.snapshot(), listener)
-    }
-
-    private FileCollection files(File... files) {
-        FileTree collection = context.mock(FileTree.class)
-        context.checking {
-            allowing(collection).getAsFileTree()
-            will(returnValue(collection))
-            allowing(collection).iterator()
-            will(returnIterator(files as List))
-        }
-        return collection
-    }
-    
-}
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection
+
+
+import static org.junit.Assert.*
+import static org.hamcrest.Matchers.*
+
+import org.gradle.util.JUnit4GroovyMockery
+import org.jmock.integration.junit4.JMock
+import org.junit.runner.RunWith
+import org.junit.Test
+import org.junit.Rule
+import org.gradle.util.TemporaryFolder
+import org.gradle.util.TestFile
+import org.gradle.api.file.FileCollection
+import org.gradle.util.ChangeListener
+import org.gradle.api.file.FileTree
+
+ at RunWith(JMock.class)
+public class DefaultFileSnapshotterTest {
+    private final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
+    private final Hasher hasher = new DefaultHasher()
+    private int counter
+    private ChangeListener listener = context.mock(ChangeListener.class)
+    private final DefaultFileSnapshotter snapshotter = new DefaultFileSnapshotter(hasher)
+    @Rule
+    public final TemporaryFolder tmpDir = new TemporaryFolder()
+
+    @Test
+    public void getFilesReturnsOnlyTheFilesWhichExisted() {
+        TestFile file = tmpDir.createFile('file1')
+        TestFile dir = tmpDir.createDir('file2')
+        TestFile noExist = tmpDir.file('file3')
+
+        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file, dir, noExist))
+
+        assertThat(snapshot.files.files as List, equalTo([file]))
+    }
+    
+    @Test
+    public void notifiesListenerWhenFileAdded() {
+        TestFile file1 = tmpDir.createFile('file1')
+        TestFile file2 = tmpDir.createFile('file2')
+
+        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file1))
+
+        context.checking {
+            one(listener).added(file2)
+        }
+        snapshotter.snapshot(files(file1, file2)).changesSince(snapshot, listener)
+    }
+
+    @Test
+    public void notifiesListenerWhenFileRemoved() {
+        TestFile file1 = tmpDir.createFile('file1')
+        TestFile file2 = tmpDir.createFile('file2')
+
+        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file1, file2))
+
+        context.checking {
+            one(listener).removed(file2)
+        }
+        snapshotter.snapshot(files(file1)).changesSince(snapshot, listener)
+    }
+
+    @Test
+    public void fileHasNotChangedWhenTypeAndHashHaveNotChanged() {
+        TestFile file = tmpDir.createFile('file')
+
+        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file))
+        assertThat(snapshot, notNullValue())
+
+        snapshotter.snapshot(files(file)).changesSince(snapshot, listener)
+    }
+
+    @Test
+    public void fileHasChangedWhenTypeHasChanged() {
+        TestFile file = tmpDir.createFile('file')
+
+        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file))
+
+        file.delete()
+        file.createDir()
+
+        context.checking {
+            one(listener).changed(file)
+        }
+        snapshotter.snapshot(files(file)).changesSince(snapshot, listener)
+    }
+
+    @Test
+    public void fileHasChangedWhenHashHasChanged() {
+        TestFile file = tmpDir.createFile('file')
+
+        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file))
+
+        file.write('new content')
+
+        context.checking {
+            one(listener).changed(file)
+        }
+        snapshotter.snapshot(files(file)).changesSince(snapshot, listener)
+    }
+
+    @Test
+    public void directoryHasNotChangedWhenTypeHasNotChanged() {
+        TestFile dir = tmpDir.createDir('dir')
+
+        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(dir))
+
+        snapshotter.snapshot(files(dir)).changesSince(snapshot, listener)
+    }
+
+    @Test
+    public void directoryHasChangedWhenTypeHasChanged() {
+        TestFile dir = tmpDir.createDir('dir')
+
+        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(dir))
+
+        dir.deleteDir()
+        dir.createFile()
+
+        context.checking {
+            one(listener).changed(dir)
+        }
+        snapshotter.snapshot(files(dir)).changesSince(snapshot, listener)
+    }
+
+    @Test
+    public void nonExistentFileUnchangedWhenTypeHasNotChanged() {
+        TestFile file = tmpDir.file('unknown')
+
+        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file))
+
+        snapshotter.snapshot(files(file)).changesSince(snapshot, listener)
+    }
+
+    @Test
+    public void nonExistentFileIsChangedWhenTypeHasChanged() {
+        TestFile file = tmpDir.file('unknown')
+
+        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file))
+
+        file.createFile()
+
+        context.checking {
+            one(listener).changed(file)
+        }
+        snapshotter.snapshot(files(file)).changesSince(snapshot, listener)
+    }
+
+    @Test
+    public void ignoresDuplicatesInFileCollection() {
+        TestFile file1 = tmpDir.createFile('file')
+        TestFile file2 = tmpDir.createFile('file')
+
+        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file1, file2))
+
+        snapshotter.snapshot(files(file1)).changesSince(snapshot, listener)
+    }
+
+    @Test
+    public void canCreateEmptySnapshot() {
+        TestFile file = tmpDir.createFile('file')
+        FileCollectionSnapshot snapshot = snapshotter.emptySnapshot()
+
+        FileCollectionSnapshot newSnapshot = snapshotter.snapshot(files(file))
+
+        context.checking {
+            one(listener).added(file)
+        }
+
+        newSnapshot.changesSince(snapshot, listener)
+    }
+
+    @Test
+    public void diffAddsAddedFilesToSnapshot() {
+        TestFile file = tmpDir.createFile('file')
+        ChangeListener<FileCollectionSnapshot.Merge> mergeListener = context.mock(ChangeListener.class)
+
+        FileCollectionSnapshot original = snapshotter.emptySnapshot()
+        FileCollectionSnapshot modified = snapshotter.snapshot(files(file))
+
+        context.checking {
+            one(mergeListener).added(withParam(notNullValue()))
+        }
+
+        FileCollectionSnapshot target = modified.changesSince(original).applyTo(snapshotter.emptySnapshot(), mergeListener)
+
+        context.checking {
+            one(listener).added(file)
+        }
+        target.changesSince(original, listener)
+    }
+
+    @Test
+    public void canIgnoreAddedFileInDiff() {
+        TestFile file = tmpDir.createFile('file')
+        ChangeListener<FileCollectionSnapshot.Merge> mergeListener = context.mock(ChangeListener.class)
+
+        FileCollectionSnapshot original = snapshotter.emptySnapshot()
+        FileCollectionSnapshot modified = snapshotter.snapshot(files(file))
+
+        context.checking {
+            one(mergeListener).added(withParam(notNullValue()))
+            will {merge -> merge.ignore()}
+        }
+
+        FileCollectionSnapshot target = modified.changesSince(original).applyTo(snapshotter.emptySnapshot(), mergeListener)
+
+        target.changesSince(original, listener)
+    }
+
+    @Test
+    public void diffAddsChangedFilesToSnapshot() {
+        TestFile file = tmpDir.createFile('file')
+        ChangeListener<FileCollectionSnapshot.Merge> mergeListener = context.mock(ChangeListener.class)
+
+        FileCollectionSnapshot original = snapshotter.snapshot(files(file))
+        file.write('new content')
+        FileCollectionSnapshot modified = snapshotter.snapshot(files(file))
+
+        context.checking {
+            one(mergeListener).changed(withParam(notNullValue()))
+        }
+
+        FileCollectionSnapshot target = modified.changesSince(original).applyTo(snapshotter.emptySnapshot(), mergeListener)
+
+        context.checking {
+            one(listener).changed(file)
+        }
+        target.changesSince(original, listener)
+    }
+
+    @Test
+    public void canIgnoreChangedFileInDiff() {
+        TestFile file = tmpDir.createFile('file')
+        ChangeListener<FileCollectionSnapshot.Merge> mergeListener = context.mock(ChangeListener.class)
+
+        FileCollectionSnapshot original = snapshotter.snapshot(files(file))
+        FileCollectionSnapshot target = snapshotter.snapshot(files(file))
+        file.write('new content')
+        FileCollectionSnapshot modified = snapshotter.snapshot(files(file))
+
+        context.checking {
+            one(mergeListener).changed(withParam(notNullValue()))
+            will {merge -> merge.ignore()}
+        }
+
+        target = modified.changesSince(original).applyTo(target, mergeListener)
+
+        target.changesSince(original, listener)
+    }
+
+    @Test
+    public void diffRemovesDeletedFilesFromSnapshot() {
+        TestFile file = tmpDir.createFile('file')
+        ChangeListener<FileCollectionSnapshot.Merge> mergeListener = context.mock(ChangeListener.class)
+
+        FileCollectionSnapshot original = snapshotter.snapshot(files(file))
+        FileCollectionSnapshot modified = snapshotter.emptySnapshot()
+
+        context.checking {
+            one(mergeListener).removed(withParam(notNullValue()))
+        }
+
+        FileCollectionSnapshot target = modified.changesSince(original).applyTo(snapshotter.snapshot(files(file)), mergeListener)
+
+        context.checking {
+            one(listener).removed(file)
+        }
+        target.changesSince(original, listener)
+    }
+
+    @Test
+    public void canIgnoreRemovedFileInDiff() {
+        TestFile file = tmpDir.createFile('file')
+        ChangeListener<FileCollectionSnapshot.Merge> mergeListener = context.mock(ChangeListener.class)
+
+        FileCollectionSnapshot original = snapshotter.snapshot(files(file))
+        FileCollectionSnapshot modified = snapshotter.emptySnapshot()
+
+        context.checking {
+            one(mergeListener).removed(withParam(notNullValue()))
+            will{merge -> merge.ignore()}
+        }
+
+        FileCollectionSnapshot target = modified.changesSince(original).applyTo(snapshotter.snapshot(files(file)), mergeListener)
+
+        target.changesSince(original, listener)
+    }
+
+    @Test
+    public void diffIgnoresUnchangedFilesInSnapshot() {
+        TestFile file = tmpDir.createFile('file')
+        ChangeListener<FileCollectionSnapshot.Merge> mergeListener = context.mock(ChangeListener.class)
+
+        FileCollectionSnapshot original = snapshotter.snapshot(files(file))
+        FileCollectionSnapshot modified = snapshotter.snapshot(files(file))
+        FileCollectionSnapshot target = modified.changesSince(original).applyTo(snapshotter.emptySnapshot(), mergeListener)
+
+        target.changesSince(snapshotter.emptySnapshot(), listener)
+    }
+
+    private FileCollection files(File... files) {
+        FileTree collection = context.mock(FileTree.class)
+        context.checking {
+            allowing(collection).getAsFileTree()
+            will(returnValue(collection))
+            allowing(collection).iterator()
+            will(returnIterator(files as List))
+        }
+        return collection
+    }
+    
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/DefaultTaskArtifactStateRepositoryTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/DefaultTaskArtifactStateRepositoryTest.java
index e195ef2..c632c1b 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/DefaultTaskArtifactStateRepositoryTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/DefaultTaskArtifactStateRepositoryTest.java
@@ -31,7 +31,7 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.File;
+import java.io.*;
 import java.util.*;
 
 import static org.gradle.util.Matchers.*;
@@ -65,7 +65,7 @@ public class DefaultTaskArtifactStateRepositoryTest {
 
     @Before
     public void setup() {
-        context.checking(new Expectations(){{
+        context.checking(new Expectations() {{
             CacheBuilder builder = context.mock(CacheBuilder.class);
 
             one(cacheRepository).cache("outputFileStates");
@@ -75,7 +75,7 @@ public class DefaultTaskArtifactStateRepositoryTest {
             will(returnValue(persistentCache));
 
             one(persistentCache).openIndexedCache();
-            will(returnValue(new TestIndexedCache()));
+            will(returnValue(new InMemoryIndexedCache()));
         }});
 
         FileSnapshotter inputFilesSnapshotter = new DefaultFileSnapshotter(new DefaultHasher());
@@ -263,7 +263,7 @@ public class DefaultTaskArtifactStateRepositoryTest {
         TaskArtifactState state = repository.getStateFor(task());
         assertFalse(state.isUpToDate());
     }
-    
+
     @Test
     public void artifactsAreNotUpToDateWhenAnyFileInInputDirChangesType() {
         execute();
@@ -328,7 +328,7 @@ public class DefaultTaskArtifactStateRepositoryTest {
 
         TaskArtifactState state = repository.getStateFor(task1);
         assertFalse(state.isUpToDate());
-        state.update();
+        state.afterTask();
 
         outputDir.deleteDir();
 
@@ -336,7 +336,7 @@ public class DefaultTaskArtifactStateRepositoryTest {
         state = repository.getStateFor(task2);
         assertFalse(state.isUpToDate());
         task2.execute();
-        state.update();
+        state.afterTask();
 
         // Task should be out-of-date
         state = repository.getStateFor(task1);
@@ -379,15 +379,15 @@ public class DefaultTaskArtifactStateRepositoryTest {
         expectEmptyCacheLocated();
 
         TaskArtifactState state = repository.getStateFor(task());
-        assertThat(state.getOutputFiles().getFiles(), isEmpty());
+        assertThat(state.getExecutionHistory().getOutputFiles().getFiles(), isEmpty());
     }
-    
+
     @Test
     public void hasTaskHistoryFromPreviousExecution() {
         execute();
 
         TaskArtifactState state = repository.getStateFor(task());
-        assertThat(state.getOutputFiles().getFiles(), equalTo(toLinkedSet((File) outputFile, outputDirFile, outputDirFile2)));
+        assertThat(state.getExecutionHistory().getOutputFiles().getFiles(), equalTo(toLinkedSet((File) outputFile, outputDirFile, outputDirFile2)));
     }
 
     @Test
@@ -439,13 +439,13 @@ public class DefaultTaskArtifactStateRepositoryTest {
 
         TaskArtifactState state = repository.getStateFor(task());
         assertTrue(state.isUpToDate());
-        assertThat(state.getOutputFiles().getFiles(), (Matcher) not(hasItem(otherFile)));
+        assertThat(state.getExecutionHistory().getOutputFiles().getFiles(), (Matcher) not(hasItem(otherFile)));
     }
 
     @Test
     public void considersExistingFileInOutputDirectoryWhichIsUpdatedByTheTaskAsProducedByTask() {
         expectEmptyCacheLocated();
-        
+
         TestFile otherFile = outputDir.file("other").createFile();
 
         TaskInternal task = task();
@@ -455,13 +455,13 @@ public class DefaultTaskArtifactStateRepositoryTest {
         task.execute();
         otherFile.write("new content");
 
-        state.update();
+        state.afterTask();
 
         otherFile.delete();
 
         state = repository.getStateFor(task());
         assertFalse(state.isUpToDate());
-        assertThat(state.getOutputFiles().getFiles(), (Matcher) hasItem(otherFile));
+        assertThat(state.getExecutionHistory().getOutputFiles().getFiles(), (Matcher) hasItem(otherFile));
     }
 
     @Test
@@ -472,14 +472,14 @@ public class DefaultTaskArtifactStateRepositoryTest {
 
         TaskArtifactState state = repository.getStateFor(task());
         assertFalse(state.isUpToDate());
-        state.update();
+        state.afterTask();
 
         outputDirFile.write("ignore me");
 
         state = repository.getStateFor(task());
         assertTrue(state.isUpToDate());
-        assertThat(state.getOutputFiles().getFiles(), (Matcher) not(hasItem(outputDirFile)));
-        state.update();
+        assertThat(state.getExecutionHistory().getOutputFiles().getFiles(), (Matcher) not(hasItem(outputDirFile)));
+        state.afterTask();
     }
 
     @Test
@@ -512,7 +512,7 @@ public class DefaultTaskArtifactStateRepositoryTest {
 
         TaskArtifactState state = repository.getStateFor(task);
         assertFalse(state.isUpToDate());
-        assertThat(state.getOutputFiles(), isEmpty());
+        assertThat(state.getExecutionHistory().getOutputFiles(), isEmpty());
     }
 
     @Test
@@ -544,11 +544,11 @@ public class DefaultTaskArtifactStateRepositoryTest {
 
         TaskArtifactState state = repository.getStateFor(instance1);
         assertTrue(state.isUpToDate());
-        assertThat(state.getOutputFiles().getFiles(), equalTo(toLinkedSet((File) outputDirFile)));
+        assertThat(state.getExecutionHistory().getOutputFiles().getFiles(), equalTo(toLinkedSet((File) outputDirFile)));
 
         state = repository.getStateFor(instance2);
         assertTrue(state.isUpToDate());
-        assertThat(state.getOutputFiles().getFiles(), equalTo(toLinkedSet((File) outputDirFile2)));
+        assertThat(state.getExecutionHistory().getOutputFiles().getFiles(), equalTo(toLinkedSet((File) outputDirFile2)));
     }
 
     private void execute() {
@@ -561,25 +561,35 @@ public class DefaultTaskArtifactStateRepositoryTest {
             TaskArtifactState state = repository.getStateFor(task);
             state.isUpToDate();
             task.execute();
-            state.update();
+            state.afterTask();
         }
     }
-    
+
     private void expectEmptyCacheLocated() {
-        context.checking(new Expectations(){{
-            CacheBuilder builder = context.mock(CacheBuilder.class);
+        context.checking(new Expectations() {{
+            CacheBuilder tasksCacheBuilder = context.mock(CacheBuilder.class);
+            CacheBuilder fileSnapshotCacheBuilder = context.mock(CacheBuilder.class);
 
             one(cacheRepository).cache("taskArtifacts");
-            will(returnValue(builder));
+            will(returnValue(tasksCacheBuilder));
 
-            one(builder).forObject(gradle);
-            will(returnValue(builder));
+            one(tasksCacheBuilder).forObject(gradle);
+            will(returnValue(tasksCacheBuilder));
 
-            one(builder).open();
+            one(tasksCacheBuilder).open();
+            will(returnValue(persistentCache));
+
+            atMost(1).of(cacheRepository).cache("fileSnapshots");
+            will(returnValue(fileSnapshotCacheBuilder));
+
+            atMost(1).of(fileSnapshotCacheBuilder).open();
             will(returnValue(persistentCache));
-            
+
             one(persistentCache).openIndexedCache(with(notNullValue(Serializer.class)));
-            will(returnValue(new TestIndexedCache()));
+            will(returnValue(new InMemoryIndexedCache()));
+
+            atMost(1).of(persistentCache).openIndexedCache();
+            will(returnValue(new InMemoryIndexedCache()));
         }});
     }
 
@@ -666,19 +676,4 @@ public class DefaultTaskArtifactStateRepositoryTest {
     public static class TaskSubType extends DefaultTask {
     }
 
-    public static class TestIndexedCache implements PersistentIndexedCache<Object, Object> {
-        Map<Object, Object> entries = new HashMap<Object, Object>();
-
-        public Object get(Object key) {
-            return entries.get(key);
-        }
-
-        public void put(Object key, Object value) {
-            entries.put(key, value);
-        }
-
-        public void remove(Object key) {
-            entries.remove(key);
-        }
-    }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/FileCacheBroadcastTaskArtifactStateRepositoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/FileCacheBroadcastTaskArtifactStateRepositoryTest.groovy
new file mode 100644
index 0000000..b7f1794
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/FileCacheBroadcastTaskArtifactStateRepositoryTest.groovy
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection
+
+import org.gradle.api.file.FileCollection
+import org.gradle.api.internal.TaskInternal
+import org.gradle.api.internal.TaskOutputsInternal
+import org.gradle.api.tasks.TaskInputs
+import spock.lang.Specification
+
+class FileCacheBroadcastTaskArtifactStateRepositoryTest extends Specification {
+    final TaskArtifactStateRepository target = Mock()
+    final TaskArtifactState targetState = Mock()
+    final TaskInternal task = Mock()
+    final TaskInputs taskInputs = Mock()
+    final TaskOutputsInternal taskOutputs = Mock()
+    final FileCollection outputs = Mock()
+    final FileCollection inputs = Mock()
+    final FileCacheListener listener = Mock()
+    final FileCacheBroadcastTaskArtifactStateRepository repository = new FileCacheBroadcastTaskArtifactStateRepository(target, listener)
+
+    def setup() {
+        _ * task.inputs >> taskInputs
+        _ * taskInputs.files >> inputs
+        _ * task.outputs >> taskOutputs
+        _ * taskOutputs.files >> outputs
+    }
+    
+    def marksTaskInputsAndOutputsAsCacheableWhenCheckingUpToDate() {
+        when:
+        def state = repository.getStateFor(task)
+        state.isUpToDate()
+
+        then:
+        1 * listener.cacheable(inputs)
+        1 * listener.cacheable(outputs)
+        1 * target.getStateFor(task) >> targetState
+        1 * targetState.isUpToDate()
+        0 * listener._
+    }
+
+    def invalidatesTaskOutputsWhenTaskIsToBeExecuted() {
+        given:
+        taskOutputs.hasOutput >> true
+
+        when:
+        def state = repository.getStateFor(task)
+        state.beforeTask()
+
+        then:
+        1 * listener.invalidate(outputs)
+        1 * target.getStateFor(task) >> targetState
+        1 * targetState.beforeTask()
+        0 * listener._
+    }
+
+    def invalidatesEverythingWhenTaskWhichDoesNotDeclareAnyOutputsIsToBeExecuted() {
+        given:
+        taskOutputs.hasOutput >> false
+        
+        when:
+        def state = repository.getStateFor(task)
+        state.beforeTask()
+
+        then:
+        1 * listener.invalidateAll()
+        1 * target.getStateFor(task) >> targetState
+        1 * targetState.beforeTask()
+        0 * listener._
+    }
+
+    def marksTaskOutputsAsCacheableAfterTaskHasExecuted() {
+        when:
+        def state = repository.getStateFor(task)
+        state.afterTask()
+
+        then:
+        1 * listener.cacheable(outputs)
+        1 * target.getStateFor(task) >> targetState
+        1 * targetState.afterTask()
+        0 * listener._
+    }
+
+    def delegatesToBackingStateForOtherMethods() {
+        when:
+        def state = repository.getStateFor(task)
+        state.finished()
+
+        then:
+        1 * target.getStateFor(task) >> targetState
+        1 * targetState.finished()
+        0 * listener._
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/ShortCircuitTaskArtifactStateRepositoryTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/ShortCircuitTaskArtifactStateRepositoryTest.java
index 73919af..7d9caba 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/ShortCircuitTaskArtifactStateRepositoryTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/ShortCircuitTaskArtifactStateRepositoryTest.java
@@ -1,120 +1,124 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection;
-
-import org.gradle.StartParameter;
-import org.gradle.api.Task;
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.internal.TaskOutputsInternal;
-import org.gradle.api.specs.Spec;
-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 static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
- at RunWith(JMock.class)
-public class ShortCircuitTaskArtifactStateRepositoryTest {
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private final StartParameter startParameter = new StartParameter();
-    private final TaskArtifactStateRepository delegate = context.mock(TaskArtifactStateRepository.class);
-    private final TaskInternal task = context.mock(TaskInternal.class);
-    private final TaskArtifactState taskArtifactState = context.mock(TaskArtifactState.class);
-    private final TaskOutputsInternal taskOutputsInternal = context.mock(TaskOutputsInternal.class);
-    private final Spec<Task> upToDateSpec = context.mock(Spec.class);
-    private final ShortCircuitTaskArtifactStateRepository repository = new ShortCircuitTaskArtifactStateRepository(startParameter, delegate);
-
-    @Before
-    public void setup() {
-        context.checking(new Expectations() {{
-            allowing(task).getOutputs();
-            will(returnValue(taskOutputsInternal));
-            allowing(taskOutputsInternal).getUpToDateSpec();
-            will(returnValue(upToDateSpec));
-        }});
-    }
-
-    @Test
-    public void delegatesToBackingRepositoryToCreateStateObject() {
-        expectTaskStateCreated();
-
-        TaskArtifactState state = repository.getStateFor(task);
-        assertNotNull(state);
-
-        final FileCollection outputFiles = context.mock(FileCollection.class);
-
-        context.checking(new Expectations() {{
-            one(taskArtifactState).getOutputFiles();
-            will(returnValue(outputFiles));
-            one(taskArtifactState).update();
-        }});
-
-        assertThat(state.getOutputFiles(), sameInstance(outputFiles));
-        state.update();
-    }
-
-    @Test
-    public void taskArtifactsAreOutOfDateWhenStartParameterOverrideIsSet() {
-        expectTaskStateCreated();
-
-        TaskArtifactState state = repository.getStateFor(task);
-
-        startParameter.setNoOpt(true);
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void taskArtifactsAreOutOfDateWhenUpToDateSpecIsFalse() {
-        expectTaskStateCreated();
-
-        TaskArtifactState state = repository.getStateFor(task);
-
-        context.checking(new Expectations() {{
-            one(upToDateSpec).isSatisfiedBy(task);
-            will(returnValue(false));
-        }});
-
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void determinesWhetherTaskArtifactsAreUpToDateUsingBackingRepository() {
-        expectTaskStateCreated();
-
-        TaskArtifactState state = repository.getStateFor(task);
-
-        context.checking(new Expectations() {{
-            one(upToDateSpec).isSatisfiedBy(task);
-            will(returnValue(true));
-            one(taskArtifactState).isUpToDate();
-            will(returnValue(true));
-        }});
-
-        assertTrue(state.isUpToDate());
-    }
-
-    private void expectTaskStateCreated() {
-        context.checking(new Expectations() {{
-            one(delegate).getStateFor(task);
-            will(returnValue(taskArtifactState));
-        }});
-    }
-}
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.changedetection;
+
+import org.gradle.StartParameter;
+import org.gradle.api.Task;
+import org.gradle.api.internal.TaskExecutionHistory;
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.TaskOutputsInternal;
+import org.gradle.api.specs.Spec;
+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 static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.*;
+
+ at RunWith(JMock.class)
+public class ShortCircuitTaskArtifactStateRepositoryTest {
+    private final JUnit4Mockery context = new JUnit4Mockery();
+    private final StartParameter startParameter = new StartParameter();
+    private final TaskArtifactStateRepository delegate = context.mock(TaskArtifactStateRepository.class);
+    private final TaskInternal task = context.mock(TaskInternal.class);
+    private final TaskArtifactState taskArtifactState = context.mock(TaskArtifactState.class);
+    private final TaskOutputsInternal taskOutputsInternal = context.mock(TaskOutputsInternal.class);
+    private final Spec<Task> upToDateSpec = context.mock(Spec.class);
+    private final ShortCircuitTaskArtifactStateRepository repository = new ShortCircuitTaskArtifactStateRepository(startParameter, delegate);
+
+    @Before
+    public void setup() {
+        context.checking(new Expectations() {{
+            allowing(task).getOutputs();
+            will(returnValue(taskOutputsInternal));
+            allowing(taskOutputsInternal).getUpToDateSpec();
+            will(returnValue(upToDateSpec));
+        }});
+    }
+
+    @Test
+    public void delegatesToBackingRepositoryToCreateStateObject() {
+        expectTaskStateCreated();
+
+        TaskArtifactState state = repository.getStateFor(task);
+        assertNotNull(state);
+
+        final TaskExecutionHistory executionHistory = context.mock(TaskExecutionHistory.class);
+
+        context.checking(new Expectations() {{
+            one(taskArtifactState).getExecutionHistory();
+            will(returnValue(executionHistory));
+            one(taskArtifactState).beforeTask();
+            one(taskArtifactState).afterTask();
+            one(taskArtifactState).finished();
+        }});
+
+        assertThat(state.getExecutionHistory(), sameInstance(executionHistory));
+        state.beforeTask();
+        state.afterTask();
+        state.finished();
+    }
+
+    @Test
+    public void taskArtifactsAreOutOfDateWhenStartParameterOverrideIsSet() {
+        expectTaskStateCreated();
+
+        TaskArtifactState state = repository.getStateFor(task);
+
+        startParameter.setNoOpt(true);
+        assertFalse(state.isUpToDate());
+    }
+
+    @Test
+    public void taskArtifactsAreOutOfDateWhenUpToDateSpecIsFalse() {
+        expectTaskStateCreated();
+
+        TaskArtifactState state = repository.getStateFor(task);
+
+        context.checking(new Expectations() {{
+            one(upToDateSpec).isSatisfiedBy(task);
+            will(returnValue(false));
+        }});
+
+        assertFalse(state.isUpToDate());
+    }
+
+    @Test
+    public void determinesWhetherTaskArtifactsAreUpToDateUsingBackingRepository() {
+        expectTaskStateCreated();
+
+        TaskArtifactState state = repository.getStateFor(task);
+
+        context.checking(new Expectations() {{
+            one(upToDateSpec).isSatisfiedBy(task);
+            will(returnValue(true));
+            one(taskArtifactState).isUpToDate();
+            will(returnValue(true));
+        }});
+
+        assertTrue(state.isUpToDate());
+    }
+
+    private void expectTaskStateCreated() {
+        context.checking(new Expectations() {{
+            one(delegate).getStateFor(task);
+            will(returnValue(taskArtifactState));
+        }});
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/AbstractFileCollectionTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/AbstractFileCollectionTest.java
index e307a3c..4f23782 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/AbstractFileCollectionTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/AbstractFileCollectionTest.java
@@ -18,28 +18,33 @@ package org.gradle.api.internal.file;
 import org.gradle.api.Task;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.file.FileTree;
+import org.gradle.api.file.FileVisitorUtil;
 import org.gradle.api.specs.Spec;
 import org.gradle.api.tasks.StopExecutionException;
 import org.gradle.api.tasks.TaskDependency;
-import org.gradle.util.TestFile;
-import org.gradle.util.HelperUtil;
-import org.gradle.util.TemporaryFolder;
-import org.hamcrest.Matcher;
+import org.gradle.util.*;
+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.util.*;
 
 import static org.gradle.api.tasks.AntBuilderAwareUtil.*;
-import static org.gradle.util.Matchers.*;
+import static org.gradle.util.Matchers.isEmpty;
 import static org.gradle.util.WrapUtil.*;
 import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.*;
 
+ at RunWith(JMock.class)
 public class AbstractFileCollectionTest {
     @Rule
-    public TemporaryFolder testDir = new TemporaryFolder();
+    public final TemporaryFolder testDir = new TemporaryFolder();
+    final JUnit4Mockery context = new JUnit4GroovyMockery();
+    final TaskDependency dependency = context.mock(TaskDependency.class);
 
     @Test
     public void usesDisplayNameAsToString() {
@@ -238,13 +243,12 @@ public class AbstractFileCollectionTest {
 
     @Test
     public void toFileTreeReturnsSingletonTreeForEachFileInCollection() {
-        File file = new File("f1");
+        File file = testDir.createFile("f1");
+        File file2 = testDir.createFile("f2");
 
-        TestFileCollection collection = new TestFileCollection(file);
+        TestFileCollection collection = new TestFileCollection(file, file2);
         FileTree tree = collection.getAsFileTree();
-        assertThat(tree, instanceOf(CompositeFileTree.class));
-        CompositeFileTree compositeTree = (CompositeFileTree) tree;
-        assertThat(compositeTree.getSourceCollections(), hasItems((Matcher) instanceOf(SingletonFileTree.class)));
+        FileVisitorUtil.assertVisits(tree, GUtil.map("f1", file, "f2", file2));
     }
 
     @Test
@@ -295,15 +299,26 @@ public class AbstractFileCollectionTest {
         TestFileCollectionWithDependency collection = new TestFileCollectionWithDependency();
         collection.files.add(new File("f1"));
 
-        assertThat(collection.getAsFileTree().getBuildDependencies(), sameInstance(collection.dependency));
-        assertThat(collection.getAsFileTree().matching(HelperUtil.TEST_CLOSURE).getBuildDependencies(), sameInstance(collection.dependency));
+        assertHasSameDependencies(collection.getAsFileTree());
+        assertHasSameDependencies(collection.getAsFileTree().matching(HelperUtil.TEST_CLOSURE));
     }
 
     @Test
     public void filteredCollectionHasSameDependenciesAsThis() {
         TestFileCollectionWithDependency collection = new TestFileCollectionWithDependency();
 
-        assertThat(collection.filter(HelperUtil.toClosure("{true}")).getBuildDependencies(), sameInstance(collection.dependency));
+        assertHasSameDependencies(collection.filter(HelperUtil.toClosure("{true}")));
+    }
+
+    private void assertHasSameDependencies(FileCollection tree) {
+        final Task task = context.mock(Task.class);
+        final Task depTask = context.mock(Task.class);
+        context.checking(new Expectations() {{
+            one(dependency).getDependencies(task);
+            will(returnValue(toSet(depTask)));
+        }});
+
+        assertThat(tree.getBuildDependencies().getDependencies(task), equalTo((Object) toSet(depTask)));
     }
 
     private class TestFileCollection extends AbstractFileCollection {
@@ -323,12 +338,6 @@ public class AbstractFileCollectionTest {
     }
 
     private class TestFileCollectionWithDependency extends TestFileCollection {
-        TaskDependency dependency = new TaskDependency() {
-            public Set<? extends Task> getDependencies(Task task) {
-                throw new UnsupportedOperationException();
-            }
-        };
-
         @Override
         public TaskDependency getBuildDependencies() {
             return dependency;
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/AbstractFileTreeTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/AbstractFileTreeTest.groovy
index 7d39b32..c57866b 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/AbstractFileTreeTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/AbstractFileTreeTest.groovy
@@ -15,46 +15,71 @@
  */
 package org.gradle.api.internal.file
 
-
-import static org.junit.Assert.*
-import static org.hamcrest.Matchers.*
-import static org.gradle.util.Matchers.*
-import org.gradle.util.JUnit4GroovyMockery
-import org.jmock.integration.junit4.JMock
-import org.junit.runner.RunWith
-import org.junit.Test
 import org.gradle.api.file.FileTree
-import org.gradle.api.file.FileVisitor
 import org.gradle.api.file.FileVisitDetails
+import org.gradle.api.file.FileVisitor
+import org.gradle.api.file.RelativePath
+import org.gradle.api.tasks.TaskDependency
+import spock.lang.Specification
 
- at RunWith(JMock.class)
-public class AbstractFileTreeTest {
-    private final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
-
-    @Test
-    public void isEmptyWhenVisitsNoFiles() {
+public class AbstractFileTreeTest extends Specification {
+    def isEmptyWhenVisitsNoFiles() {
         def tree = new TestFileTree([])
-        assertTrue(tree.empty)
+
+        expect:
+        tree.empty
     }
 
-    @Test
-    public void isNotEmptyWhenVisitsFirstFile() {
-        FileVisitDetails file = context.mock(FileVisitDetails.class)
+    def isNotEmptyWhenVisitsFirstFile() {
+        FileVisitDetails file = Mock()
         def tree = new TestFileTree([file])
 
-        context.checking {
-            one(file).stopVisiting()
-        }
+        when:
+        def empty = tree.empty
+
+        then:
+        !empty
+        1 * file.stopVisiting()
+    }
+
+    def canFilterTreeUsingClosure() {
+        FileVisitDetails file1 = Mock()
+        FileVisitDetails file2 = Mock()
+        FileVisitor visitor = Mock()
+        def tree = new TestFileTree([file1, file2])
+
+        given:
+        _ * file1.relativePath >> new RelativePath(true, 'a.txt')
+        _ * file2.relativePath >> new RelativePath(true, 'b.html')
+
+        when:
+        def filtered = tree.matching { include '*.txt' }
+        filtered.visit(visitor)
+
+        then:
+        1 * visitor.visitFile(file1)
+        0 * visitor._
+    }
+
+    def filteredTreeHasSameDependenciesAsThis() {
+        TaskDependency buildDependencies = Mock()
+        def tree = new TestFileTree([], buildDependencies)
+
+        when:
+        def filtered = tree.matching { include '*.txt' }
 
-        assertFalse(tree.empty)
+        then:
+        filtered.buildDependencies == buildDependencies
     }
 }
 
 class TestFileTree extends AbstractFileTree {
     List contents
+    TaskDependency buildDependencies
 
-    def TestFileTree(List files) {
+    def TestFileTree(List files, TaskDependency dependencies = null) {
         this.contents = files
+        this.buildDependencies = dependencies
     }
 
     String getDisplayName() {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/BaseDirConverterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/BaseDirConverterTest.groovy
index 089619e..c29e9f3 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/BaseDirConverterTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/BaseDirConverterTest.groovy
@@ -29,6 +29,7 @@ import static org.hamcrest.Matchers.*
 import static org.junit.Assert.*
 import java.util.concurrent.Callable
 import org.gradle.util.OperatingSystem
+import org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection
 
 /**
  * @author Hans Dockter
@@ -240,8 +241,8 @@ class BaseDirConverterTest {
 
     @Test public void testFiles() {
         FileCollection collection = baseDirConverter.resolveFiles('a', 'b')
-        assertThat(collection, instanceOf(PathResolvingFileCollection))
-        assertThat(collection.sources, equalTo(['a', 'b']))
+        assertThat(collection, instanceOf(DefaultConfigurableFileCollection))
+        assertThat(collection.from, equalTo(['a', 'b'] as LinkedHashSet))
     }
 
     @Test public void testFilesReturnsSourceFileCollection() {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/CompositeFileCollectionTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/CompositeFileCollectionTest.java
index 5be94b7..bdbd2b2 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/CompositeFileCollectionTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/CompositeFileCollectionTest.java
@@ -18,12 +18,14 @@ package org.gradle.api.internal.file;
 import org.gradle.api.Task;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.file.FileTree;
+import org.gradle.api.internal.file.collections.*;
 import org.gradle.api.specs.Spec;
 import org.gradle.api.tasks.TaskDependency;
+import org.gradle.util.HelperUtil;
+import org.gradle.util.JUnit4GroovyMockery;
 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;
 
@@ -31,14 +33,13 @@ import java.io.File;
 import java.util.*;
 
 import static org.gradle.util.WrapUtil.*;
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.instanceOf;
 import static org.junit.Assert.*;
 
 @RunWith(JMock.class)
 public class CompositeFileCollectionTest {
-    private final JUnit4Mockery context = new JUnit4Mockery(){{
-        setImposteriser(ClassImposteriser.INSTANCE);
-    }};
+    private final JUnit4Mockery context = new JUnit4GroovyMockery();
     private final AbstractFileCollection source1 = context.mock(AbstractFileCollection.class, "source1");
     private final AbstractFileCollection source2 = context.mock(AbstractFileCollection.class, "source2");
     private final TestCompositeFileCollection collection = new TestCompositeFileCollection(source1, source2);
@@ -143,9 +144,9 @@ public class CompositeFileCollectionTest {
     }
 
     @Test
-    public void getAsFileSetsReturnsUnionOfFileSets() {
-        final DefaultConfigurableFileTree set1 = new DefaultConfigurableFileTree(new File("dir1").getAbsoluteFile(), null, null);
-        final DefaultConfigurableFileTree set2 = new DefaultConfigurableFileTree(new File("dir2").getAbsoluteFile(), null, null);
+    public void getAsFileTreesReturnsUnionOfFileTrees() {
+        final DirectoryFileTree set1 = new DirectoryFileTree(new File("dir1").getAbsoluteFile());
+        final DirectoryFileTree set2 = new DirectoryFileTree(new File("dir2").getAbsoluteFile());
 
         context.checking(new Expectations() {{
             one(source1).getAsFileTrees();
@@ -158,59 +159,64 @@ public class CompositeFileCollectionTest {
     
     @Test
     public void getAsFileTreeDelegatesToEachSet() {
-        final FileTree tree1 = context.mock(FileTree.class, "tree1");
-        final FileTree tree2 = context.mock(FileTree.class, "tree2");
+        final File file1 = new File("dir1");
+        final File file2 = new File("dir2");
+
+        FileTree fileTree = collection.getAsFileTree();
+        assertThat(fileTree, instanceOf(CompositeFileTree.class));
 
         context.checking(new Expectations() {{
-            one(source1).getAsFileTree();
-            will(returnValue(tree1));
-            one(source2).getAsFileTree();
-            will(returnValue(tree2));
+            one(source1).getFiles();
+            will(returnValue(toSet(file1)));
+            one(source2).getFiles();
+            will(returnValue(toSet(file2)));
         }});
 
-        FileTree fileTree = collection.getAsFileTree();
-        assertThat(fileTree, instanceOf(CompositeFileTree.class));
-        assertThat(((CompositeFileTree) fileTree).getSourceCollections(), equalTo((Iterable) toList(tree1, tree2)));
+        ((CompositeFileTree) fileTree).getSourceCollections();
     }
 
     @Test
     public void fileTreeIsLive() {
-        final FileTree tree1 = context.mock(FileTree.class, "tree1");
-        final FileTree tree2 = context.mock(FileTree.class, "tree2");
-        final FileCollection source3 = context.mock(FileCollection.class);
-        final FileTree tree3 = context.mock(FileTree.class);
+        final File dir1 = new File("dir1");
+        final File dir2 = new File("dir1");
+        final File dir3 = new File("dir1");
+        final MinimalFileSet source3 = context.mock(MinimalFileSet.class);
+
+        FileTree fileTree = collection.getAsFileTree();
+        assertThat(fileTree, instanceOf(CompositeFileTree.class));
 
         context.checking(new Expectations() {{
-            one(source1).getAsFileTree();
-            will(returnValue(tree1));
-            one(source2).getAsFileTree();
-            will(returnValue(tree2));
+            one(source1).getFiles();
+            will(returnValue(toSet(dir1)));
+            one(source2).getFiles();
+            will(returnValue(toSet(dir2)));
         }});
 
-        FileTree fileTree = collection.getAsFileTree();
-        assertThat(fileTree, instanceOf(CompositeFileTree.class));
-        assertThat(((CompositeFileTree) fileTree).getSourceCollections(), equalTo((Iterable) toList(tree1, tree2)));
+        ((CompositeFileTree) fileTree).getSourceCollections();
 
         collection.sourceCollections.add(source3);
 
         context.checking(new Expectations() {{
-            one(source1).getAsFileTree();
-            will(returnValue(tree1));
-            one(source2).getAsFileTree();
-            will(returnValue(tree2));
-            one(source3).getAsFileTree();
-            will(returnValue(tree3));
+            one(source1).getFiles();
+            will(returnValue(toSet(dir1)));
+            one(source2).getFiles();
+            will(returnValue(toSet(dir2)));
+            one(source3).getFiles();
+            will(returnValue(toSet(dir3)));
         }});
 
-        assertThat(((CompositeFileTree) fileTree).getSourceCollections(), equalTo((Iterable) toList(tree1, tree2, tree3)));
+        ((CompositeFileTree) fileTree).getSourceCollections();
     }
 
     @Test
     public void filterDelegatesToEachSet() {
-        final FileCollection filtered1 = context.mock(FileCollection.class, "filtered1");
-        final FileCollection filtered2 = context.mock(FileCollection.class, "filtered2");
+        final FileCollection filtered1 = context.mock(FileCollection.class);
+        final FileCollection filtered2 = context.mock(FileCollection.class);
         final Spec spec = context.mock(Spec.class);
 
+        FileCollection filtered = collection.filter(spec);
+        assertThat(filtered, instanceOf(CompositeFileCollection.class));
+
         context.checking(new Expectations() {{
             one(source1).filter(spec);
             will(returnValue(filtered1));
@@ -218,8 +224,6 @@ public class CompositeFileCollectionTest {
             will(returnValue(filtered2));
         }});
 
-        FileCollection filtered = collection.filter(spec);
-        assertThat(filtered, instanceOf(CompositeFileCollection.class));
         assertThat(((CompositeFileCollection) filtered).getSourceCollections(), equalTo((Iterable) toList(filtered1, filtered2)));
     }
 
@@ -258,11 +262,45 @@ public class CompositeFileCollectionTest {
         assertThat(dependency.getDependencies(target), equalTo((Set) toSet(task1, task2, task3)));
     }
 
+    @Test
+    public void fileTreeDependsOnUnionOfAllDependencies() {
+        assertDependsOnUnionOfSourceCollections(collection.getAsFileTree());
+        assertDependsOnUnionOfSourceCollections(collection.getAsFileTree().matching(HelperUtil.TEST_CLOSURE));
+    }
+
+    @Test
+    public void filteredCollectionDependsOnUnionOfAllDependencies() {
+        assertDependsOnUnionOfSourceCollections(collection.filter(HelperUtil.TEST_CLOSURE));
+    }
+
+    private void assertDependsOnUnionOfSourceCollections(FileCollection collection) {
+        final Task target = context.mock(Task.class, "target");
+        final Task task1 = context.mock(Task.class, "task1");
+        final Task task2 = context.mock(Task.class, "task2");
+
+        context.checking(new Expectations(){{
+            TaskDependency dependency1 = context.mock(TaskDependency.class, "dep1");
+            TaskDependency dependency2 = context.mock(TaskDependency.class, "dep2");
+
+            one(source1).getBuildDependencies();
+            will(returnValue(dependency1));
+            one(dependency1).getDependencies(target);
+            will(returnValue(toSet(task1)));
+            one(source2).getBuildDependencies();
+            will(returnValue(dependency2));
+            one(dependency2).getDependencies(target);
+            will(returnValue(toSet(task2)));
+        }});
+
+        TaskDependency dependency = collection.getBuildDependencies();
+        assertThat(dependency.getDependencies(target), equalTo((Set) toSet(task1, task2)));
+    }
+
     private class TestCompositeFileCollection extends CompositeFileCollection {
-        private List<FileCollection> sourceCollections;
+        private List<Object> sourceCollections;
 
         public TestCompositeFileCollection(FileCollection... sourceCollections) {
-            this.sourceCollections = new ArrayList<FileCollection>(Arrays.asList(sourceCollections));
+            this.sourceCollections = new ArrayList<Object>(Arrays.asList(sourceCollections));
         }
 
         @Override
@@ -271,8 +309,8 @@ public class CompositeFileCollectionTest {
         }
 
         @Override
-        protected void addSourceCollections(Collection<FileCollection> sources) {
-            sources.addAll(sourceCollections);
+        public void resolve(FileCollectionResolveContext context) {
+            context.add(sourceCollections);
         }
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/CompositeFileTreeTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/CompositeFileTreeTest.java
index a219764..5bf5d8c 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/CompositeFileTreeTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/CompositeFileTreeTest.java
@@ -18,11 +18,13 @@ package org.gradle.api.internal.file;
 import groovy.lang.Closure;
 import org.gradle.api.file.FileTree;
 import org.gradle.api.file.FileVisitor;
-import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.collections.FileCollectionResolveContext;
 import org.gradle.api.tasks.util.PatternSet;
 import org.gradle.util.HelperUtil;
 import static org.gradle.util.WrapUtil.*;
 import static org.hamcrest.Matchers.*;
+
+import org.gradle.util.JUnit4GroovyMockery;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.integration.junit4.JUnit4Mockery;
@@ -30,13 +32,11 @@ import static org.junit.Assert.*;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.Collection;
-
 @RunWith(JMock.class)
 public class CompositeFileTreeTest {
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private final FileTree source1 = context.mock(FileTree.class, "source1");
-    private final FileTree source2 = context.mock(FileTree.class, "source2");
+    private final JUnit4Mockery context = new JUnit4GroovyMockery();
+    private final FileTree source1 = context.mock(FileTree.class);
+    private final FileTree source2 = context.mock(FileTree.class);
     private final CompositeFileTree tree = new CompositeFileTree() {
         @Override
         public String getDisplayName() {
@@ -44,17 +44,17 @@ public class CompositeFileTreeTest {
         }
 
         @Override
-        protected void addSourceCollections(Collection<FileCollection> sources) {
-            sources.add(source1);
-            sources.add(source2);
+        public void resolve(FileCollectionResolveContext context) {
+            context.add(source1);
+            context.add(source2);
         }
     };
 
     @Test
     public void matchingWithClosureReturnsUnionOfFilteredSets() {
         final Closure closure = HelperUtil.TEST_CLOSURE;
-        final FileTree filtered1 = context.mock(FileTree.class, "filtered1");
-        final FileTree filtered2 = context.mock(FileTree.class, "filtered2");
+        final FileTree filtered1 = context.mock(FileTree.class);
+        final FileTree filtered2 = context.mock(FileTree.class);
 
         context.checking(new Expectations() {{
             one(source1).matching(closure);
@@ -67,14 +67,14 @@ public class CompositeFileTreeTest {
         assertThat(filtered, instanceOf(CompositeFileTree.class));
         CompositeFileTree filteredCompositeSet = (CompositeFileTree) filtered;
 
-        assertThat(toList(filteredCompositeSet.getSourceCollections()), equalTo(toList(filtered1, filtered2)));
+        assertThat(toList(filteredCompositeSet.getSourceCollections()), equalTo(toList((FileTree)filtered1, filtered2)));
     }
 
     @Test
     public void matchingWithPatternSetReturnsUnionOfFilteredSets() {
         final PatternSet patternSet = new PatternSet();
-        final FileTree filtered1 = context.mock(FileTree.class, "filtered1");
-        final FileTree filtered2 = context.mock(FileTree.class, "filtered2");
+        final FileTree filtered1 = context.mock(FileTree.class);
+        final FileTree filtered2 = context.mock(FileTree.class);
 
         context.checking(new Expectations() {{
             one(source1).matching(patternSet);
@@ -87,17 +87,17 @@ public class CompositeFileTreeTest {
         assertThat(filtered, instanceOf(CompositeFileTree.class));
         CompositeFileTree filteredCompositeSet = (CompositeFileTree) filtered;
 
-        assertThat(toList(filteredCompositeSet.getSourceCollections()), equalTo(toList(filtered1, filtered2)));
+        assertThat(toList(filteredCompositeSet.getSourceCollections()), equalTo(toList((FileTree) filtered1, filtered2)));
     }
 
     @Test
     public void plusReturnsUnionOfThisTreeAndSourceTree() {
-        FileTree other = context.mock(FileTree.class, "other");
+        FileTree other = context.mock(FileTree.class);
 
         FileTree sum = tree.plus(other);
         assertThat(sum, instanceOf(CompositeFileTree.class));
         UnionFileTree sumCompositeTree = (UnionFileTree) sum;
-        assertThat(sumCompositeTree.getSourceCollections(), equalTo((Iterable) toList(tree, other)));
+        assertThat(sumCompositeTree.getSourceCollections(), equalTo((Iterable) toList(source1, source2, other)));
     }
 
     @Test
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultDirectoryWalkerTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultDirectoryWalkerTest.java
deleted file mode 100644
index f71c309..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultDirectoryWalkerTest.java
+++ /dev/null
@@ -1,288 +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.file;
-
-import org.gradle.api.file.FileVisitDetails;
-import org.gradle.api.file.RelativePath;
-import org.gradle.api.internal.file.copy.CopySpecVisitor;
-import org.gradle.api.specs.Spec;
-import org.gradle.api.tasks.util.PatternSet;
-import org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
-import org.jmock.Expectations;
-import org.jmock.Mockery;
-import org.jmock.Sequence;
-import org.jmock.api.Action;
-import org.jmock.api.Invocation;
-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 java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
- at RunWith(JMock.class)
-public class DefaultDirectoryWalkerTest {
-    private JUnit4Mockery context = new JUnit4Mockery() {{
-        setImposteriser(ClassImposteriser.INSTANCE);
-    }};
-    private CopySpecVisitor visitor;
-    private DirectoryWalker walker;
-
-    @Before
-    public void setUp() {
-        visitor = context.mock(CopySpecVisitor.class);
-    }
-
-    @Test public void rootDirEmpty() throws IOException {
-        final MockFile root = new MockFile(context, "root", false);
-
-        walker = new DefaultDirectoryWalker(visitor);
-        root.setExpectations();
-
-        walker.start(root.getMock());
-    }
-
-    @Test public void testUsesSpecFromPatternSetToMatchFilesAndDirs() {
-        final PatternSet patternSet = context.mock(PatternSet.class);
-        final Spec spec = context.mock(Spec.class);
-
-        context.checking(new Expectations(){{
-            one(patternSet).getAsSpec();
-            will(returnValue(spec));
-        }});
-
-        walker = new DefaultDirectoryWalker(visitor);
-        walker.match(patternSet);
-    }
-
-    @Test public void walkSingleFile() throws IOException {
-        walker = new DefaultDirectoryWalker(visitor);
-
-        final MockFile root = new MockFile(context, "root", false);
-        final MockFile fileToCopy = root.addFile("file.txt");
-
-        fileToCopy.setExpectations();
-
-        context.checking(new Expectations() {{
-            one(visitor).visitFile(with(file(fileToCopy)));
-        }});
-
-        walker.start(fileToCopy.getMock());
-    }
-
-    /*
-    mock file structure:
-    root
-        rootFile1
-        dir1
-           dirFile1
-           dirFile2
-        rootFile2
-
-        Test that the files are really walked breadth first
-     */
-    @Test public void walkBreadthFirst() throws IOException {
-
-        walker = new DefaultDirectoryWalker(visitor);
-
-        final MockFile root = new MockFile(context, "root", false);
-        final MockFile rootFile1 = root.addFile("rootFile1");
-        final MockFile dir1 = root.addDir("dir1");
-        final MockFile dirFile1 = dir1.addFile("dirFile1");
-        final MockFile dirFile2 = dir1.addFile("dirFile2");
-        final MockFile rootFile2 = root.addFile("rootFile2");
-        root.setExpectations();
-
-        final Sequence visiting = context.sequence("visiting");
-        context.checking(new Expectations() {{
-            one(visitor).visitFile(with(file(rootFile1))); inSequence(visiting);
-            one(visitor).visitFile(with(file(rootFile2))); inSequence(visiting);
-            one(visitor).visitDir(with(file(dir1))); inSequence(visiting);
-            one(visitor).visitFile(with(file(dirFile1))); inSequence(visiting);
-            one(visitor).visitFile(with(file(dirFile2))); inSequence(visiting);
-        }});
-
-        walker.start(root.getMock());
-    }
-
-    @Test public void walkDepthFirst() throws IOException {
-
-        walker = new DefaultDirectoryWalker(visitor).depthFirst();
-
-        final MockFile root = new MockFile(context, "root", false);
-        final MockFile rootFile1 = root.addFile("rootFile1");
-        final MockFile dir1 = root.addDir("dir1");
-        final MockFile dirFile1 = dir1.addFile("dirFile1");
-        final MockFile dirFile2 = dir1.addFile("dirFile2");
-        final MockFile rootFile2 = root.addFile("rootFile2");
-        root.setExpectations();
-
-        final Sequence visiting = context.sequence("visiting");
-        context.checking(new Expectations() {{
-            one(visitor).visitFile(with(file(rootFile1))); inSequence(visiting);
-            one(visitor).visitFile(with(file(rootFile2))); inSequence(visiting);
-            one(visitor).visitFile(with(file(dirFile1))); inSequence(visiting);
-            one(visitor).visitFile(with(file(dirFile2))); inSequence(visiting);
-            one(visitor).visitDir(with(file(dir1))); inSequence(visiting);
-        }});
-
-        walker.start(root.getMock());
-    }
-
-    @Test public void canVisitorCanStopVisit() throws IOException {
-
-        walker = new DefaultDirectoryWalker(visitor);
-
-        final MockFile root = new MockFile(context, "root", false);
-        final MockFile rootFile1 = root.addFile("rootFile1");
-        final MockFile dir1 = root.addDir("dir1");
-        final MockFile dirFile1 = dir1.addFile("dirFile1");
-        dir1.addFile("dirFile2");
-        dir1.addDir("dir1Dir").addFile("dir1Dir1File1");
-        final MockFile rootFile2 = root.addFile("rootFile2");
-        root.setExpectations();
-
-        context.checking(new Expectations() {{
-            one(visitor).visitFile(with(file(rootFile1))); will(stopVisiting());
-        }});
-
-        walker.start(root.getMock());
-
-        final Sequence visiting = context.sequence("visiting");
-        context.checking(new Expectations() {{
-            one(visitor).visitFile(with(file(rootFile1))); inSequence(visiting);
-            one(visitor).visitFile(with(file(rootFile2))); inSequence(visiting);
-            one(visitor).visitDir(with(file(dir1))); inSequence(visiting);
-            one(visitor).visitFile(with(file(dirFile1))); will(stopVisiting()); inSequence(visiting);
-        }});
-
-        walker.start(root.getMock());
-    }
-
-    private Action stopVisiting() {
-        return new Action() {
-            public void describeTo(Description description) {
-                description.appendText("stop visiting");
-            }
-
-            public Object invoke(Invocation invocation) throws Throwable {
-                FileVisitDetails details = (FileVisitDetails) invocation.getParameter(0);
-                details.stopVisiting();
-                return null;
-            }
-        };
-    }
-
-    // test excludes, includes
-
-    private Matcher<FileVisitDetails> file(final MockFile file) {
-        return new BaseMatcher<FileVisitDetails>() {
-            public boolean matches(Object o) {
-                FileVisitDetails details = (FileVisitDetails) o;
-                return details.getFile().equals(file.getMock()) && details.getRelativePath().equals(file.getRelativePath());
-            }
-
-            public void describeTo(Description description) {
-                description.appendText("details match file ").appendValue(file.getMock()).appendText(" with path ")
-                        .appendValue(file.getRelativePath());
-            }
-        };
-    }
-
-    public class MockFile {
-        private boolean isFile;
-        private String name;
-        private Mockery context;
-        private List<MockFile> children;
-        private File mock;
-        private MockFile parent;
-
-        public MockFile(Mockery context, String name, boolean isFile) {
-            this.context = context;
-            this.name = name;
-            this.isFile = isFile;
-            children = new ArrayList<MockFile>();
-            mock = context.mock(File.class, name);
-        }
-
-        public File getMock() {
-            return mock;
-        }
-
-        public MockFile addFile(String name) {
-            MockFile child = new MockFile(context, name, true);
-            child.setParent(this);
-            children.add(child);
-            return child;
-        }
-
-        public MockFile addDir(String name) {
-            MockFile child = new MockFile(context, name, false);
-            child.setParent(this);
-            children.add(child);
-            return child;
-        }
-
-        public void setParent(MockFile parent) {
-            this.parent = parent;
-        }
-
-        public RelativePath getRelativePath() {
-            if (parent == null) {
-                return new RelativePath(isFile);
-            } else {
-                return parent.getRelativePath().append(isFile, name);
-            }
-        }
-
-        public void setExpectations() {
-            Expectations expectations = new Expectations();
-            setExpectations(expectations);
-            context.checking(expectations);
-        }
-
-        public void setExpectations(Expectations expectations) {
-            try {
-                expectations.allowing(mock).getCanonicalFile();
-                expectations.will(expectations.returnValue(mock));
-            } catch (IOException th) {
-                // ignore
-            }
-            expectations.allowing(mock).isFile();
-            expectations.will(expectations.returnValue(isFile));
-            expectations.allowing(mock).getName();
-            expectations.will(expectations.returnValue(name));
-            expectations.allowing(mock).exists();
-            expectations.will(expectations.returnValue(true));
-
-            ArrayList<File> mockChildren = new ArrayList<File>(children.size());
-            for (MockFile child : children) {
-                mockChildren.add(child.getMock());
-                child.setExpectations(expectations);
-            }
-            expectations.allowing(mock).listFiles();
-            expectations.will(expectations.returnValue(mockChildren.toArray(new File[mockChildren.size()])));
-        }
-    }
-
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultFileOperationsTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultFileOperationsTest.groovy
index b8b5898..1729854 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultFileOperationsTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultFileOperationsTest.groovy
@@ -24,18 +24,20 @@ import org.gradle.api.file.ConfigurableFileCollection
 import org.gradle.api.file.FileTree
 import org.gradle.api.internal.file.archive.TarFileTree
 import org.gradle.api.internal.file.archive.ZipFileTree
+import org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection
+import org.gradle.api.internal.file.collections.FileTreeAdapter
 import org.gradle.api.internal.file.copy.CopyActionImpl
 import org.gradle.api.internal.file.copy.CopySpecImpl
 import org.gradle.api.internal.tasks.TaskResolver
+import org.gradle.process.ExecResult
+import org.gradle.process.internal.ExecException
+import org.gradle.util.ClasspathUtil
+import org.gradle.util.OperatingSystem
 import org.gradle.util.TemporaryFolder
 import org.gradle.util.TestFile
-import org.gradle.process.internal.ExecException
-import org.gradle.process.ExecResult
 import org.junit.Rule
 import org.junit.Test
 import spock.lang.Specification
-import org.gradle.util.OperatingSystem
-import org.gradle.util.ClasspathUtil
 
 public class DefaultFileOperationsTest extends Specification {
     private final FileResolver resolver = Mock()
@@ -74,8 +76,8 @@ public class DefaultFileOperationsTest extends Specification {
         def fileCollection = fileOperations.files('a', 'b')
 
         then:
-        fileCollection instanceof PathResolvingFileCollection
-        fileCollection.sources == ['a', 'b']
+        fileCollection instanceof DefaultConfigurableFileCollection
+        fileCollection.from == ['a', 'b'] as LinkedHashSet
         fileCollection.resolver.is(resolver)
         fileCollection.buildDependency.resolver.is(taskResolver)
     }
@@ -124,7 +126,8 @@ public class DefaultFileOperationsTest extends Specification {
         def zipTree = fileOperations.zipTree('path')
 
         then:
-        zipTree instanceof ZipFileTree
+        zipTree instanceof FileTreeAdapter
+        zipTree.tree instanceof ZipFileTree
     }
 
     def createsTarFileTree() {
@@ -135,7 +138,8 @@ public class DefaultFileOperationsTest extends Specification {
         def tarTree = fileOperations.tarTree('path')
 
         then:
-        tarTree instanceof TarFileTree
+        tarTree instanceof FileTreeAdapter
+        tarTree.tree instanceof TarFileTree
     }
 
     def copiesFiles() {
@@ -157,7 +161,7 @@ public class DefaultFileOperationsTest extends Specification {
 
     def deletes() {
         TestFile fileToBeDeleted = tmpDir.file("file")
-        ConfigurableFileCollection fileCollection = new PathResolvingFileCollection(resolver, null, "file")
+        ConfigurableFileCollection fileCollection = new DefaultConfigurableFileCollection(resolver, null, "file")
         resolver.resolveFiles(["file"] as Object[]) >> fileCollection
         resolver.resolve("file") >> fileToBeDeleted
         fileToBeDeleted.touch();
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultSourceDirectorySetTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultSourceDirectorySetTest.groovy
index cb4486d..29338cf 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultSourceDirectorySetTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultSourceDirectorySetTest.groovy
@@ -17,70 +17,129 @@ package org.gradle.api.internal.file
 
 import org.gradle.api.InvalidUserDataException
 import org.gradle.api.file.FileTree
+import org.gradle.api.file.SourceDirectorySet
 import org.gradle.api.tasks.StopExecutionException
 import org.gradle.util.GFileUtils
-import org.gradle.util.JUnit4GroovyMockery
 import org.gradle.util.TemporaryFolder
-import org.jmock.integration.junit4.JMock
-import org.junit.Before
+import org.gradle.util.TestFile
 import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import static org.gradle.api.tasks.AntBuilderAwareUtil.*
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*;
-
- at RunWith (JMock)
-public class DefaultSourceDirectorySetTest {
-    private final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
+import spock.lang.Specification
+import static org.gradle.api.tasks.AntBuilderAwareUtil.assertSetContainsForAllTypes
+import static org.hamcrest.Matchers.equalTo
+
+public class DefaultSourceDirectorySetTest extends Specification {
     @Rule public TemporaryFolder tmpDir = new TemporaryFolder()
-    private final File testDir = tmpDir.dir
+    private final TestFile testDir = tmpDir.dir
     private FileResolver resolver
     private DefaultSourceDirectorySet set
 
-    @Before
-    public void setUp() {
+    public void setup() {
         resolver = {src -> src instanceof File ? src : new File(testDir, src as String)} as FileResolver
         set = new DefaultSourceDirectorySet('<display-name>', resolver)
     }
 
-    @Test
-    public void addsResolvedSourceDirectoryToSet() {
+    public void addsResolvedSourceDirectory() {
+        when:
         set.srcDir 'dir1'
 
-        assertThat(set.srcDirs, equalTo([new File(testDir, 'dir1')] as Set))
+        then:
+        set.srcDirs equalTo([new File(testDir, 'dir1')] as Set)
+    }
+
+    public void addsResolvedSourceDirectories() {
+        when:
+        set.srcDir {-> ['dir1', 'dir2'] }
+
+        then:
+        set.srcDirs equalTo([new File(testDir, 'dir1'), new File(testDir, 'dir2')] as Set)
     }
 
-    @Test
-    public void addsResolvedSourceDirectoriesToSet() {
-        set.srcDir { -> ['dir1', 'dir2'] }
+    public void addsNestedDirectorySet() {
+        SourceDirectorySet nested = new DefaultSourceDirectorySet('<nested>', resolver)
+        nested.srcDir 'dir1'
 
-        assertThat(set.srcDirs, equalTo([new File(testDir, 'dir1'), new File(testDir, 'dir2')] as Set))
+        when:
+        set.source nested
+
+        then:
+        set.srcDirs equalTo([new File(testDir, 'dir1')] as Set)
     }
 
-    @Test
-    public void canSetSourceDirectories() {
-        set.srcDir 'ignore me'
+    public void settingSourceDirsReplacesExistingContent() {
+        SourceDirectorySet nested = new DefaultSourceDirectorySet('<nested>', resolver)
+        nested.srcDir 'ignore me'
+        set.srcDir 'ignore me as well'
+        set.source nested
+
+        when:
         set.srcDirs = ['dir1', 'dir2']
 
-        assertThat(set.srcDirs, equalTo([new File(testDir, 'dir1'), new File(testDir, 'dir2')] as Set))
+        then:
+        set.srcDirs equalTo([new File(testDir, 'dir1'), new File(testDir, 'dir2')] as Set)
     }
 
-    @Test
-    public void addsFilesetForEachSourceDirectory() {
+    public void containsFilesFromEachSourceDirectory() {
         File srcDir1 = new File(testDir, 'dir1')
         GFileUtils.touch(new File(srcDir1, 'subdir/file1.txt'))
         GFileUtils.touch(new File(srcDir1, 'subdir/file2.txt'))
         File srcDir2 = new File(testDir, 'dir2')
         GFileUtils.touch(new File(srcDir2, 'subdir2/file1.txt'))
 
+        when:
         set.srcDir 'dir1'
         set.srcDir 'dir2'
 
+        then:
         assertSetContainsForAllTypes(set, 'subdir/file1.txt', 'subdir/file2.txt', 'subdir2/file1.txt')
     }
 
-    @Test
+    public void convertsSourceDirectoriesToDirectoryTrees() {
+        when:
+        set.srcDir 'dir1'
+        set.srcDir 'dir2'
+        set.include 'includes'
+        set.exclude 'excludes'
+        def trees = set.srcDirTrees as List
+
+        then:
+        trees.size() == 2
+        trees[0].dir == testDir.file('dir1')
+        trees[0].patterns.includes as List == ['includes']
+        trees[0].patterns.excludes as List == ['excludes']
+        trees[1].dir == testDir.file('dir2')
+        trees[1].patterns.includes as List == ['includes']
+        trees[1].patterns.excludes as List == ['excludes']
+    }
+
+    public void convertsNestedDirectorySetsToDirectoryTrees() {
+        SourceDirectorySet nested = new DefaultSourceDirectorySet('<nested>', resolver)
+        nested.srcDirs 'dir1', 'dir2'
+
+        when:
+        set.source nested
+        def trees = set.srcDirTrees as List
+
+        then:
+        trees.size() == 2
+        trees[0].dir == testDir.file('dir1')
+        trees[1].dir == testDir.file('dir2')
+    }
+
+    public void removesDuplicateDirectoryTrees() {
+        SourceDirectorySet nested = new DefaultSourceDirectorySet('<nested>', resolver)
+        nested.srcDirs 'dir1', 'dir2'
+
+        when:
+        set.source nested
+        set.srcDir 'dir1'
+        def trees = set.srcDirTrees as List
+
+        then:
+        trees.size() == 2
+        trees[0].dir == testDir.file('dir1')
+        trees[1].dir == testDir.file('dir2')
+    }
+
     public void canUsePatternsToFilterCertainFiles() {
         File srcDir1 = new File(testDir, 'dir1')
         GFileUtils.touch(new File(srcDir1, 'subdir/file1.txt'))
@@ -91,15 +150,16 @@ public class DefaultSourceDirectorySetTest {
         GFileUtils.touch(new File(srcDir2, 'subdir2/file2.txt'))
         GFileUtils.touch(new File(srcDir2, 'subdir2/ignored.txt'))
 
+        when:
         set.srcDir 'dir1'
         set.srcDir 'dir2'
         set.include '**/file*'
         set.exclude '**/file2*'
 
+        then:
         assertSetContainsForAllTypes(set, 'subdir/file1.txt', 'subdir2/file1.txt')
     }
 
-    @Test
     public void canUseFilterPatternsToFilterCertainFiles() {
         File srcDir1 = new File(testDir, 'dir1')
         GFileUtils.touch(new File(srcDir1, 'subdir/file1.txt'))
@@ -110,93 +170,92 @@ public class DefaultSourceDirectorySetTest {
         GFileUtils.touch(new File(srcDir2, 'subdir2/file2.txt'))
         GFileUtils.touch(new File(srcDir2, 'subdir2/ignored.txt'))
 
+        when:
         set.srcDir 'dir1'
         set.srcDir 'dir2'
         set.filter.include '**/file*'
         set.filter.exclude '**/file2*'
 
+        then:
         assertSetContainsForAllTypes(set, 'subdir/file1.txt', 'subdir2/file1.txt')
     }
 
-    @Test
     public void ignoresSourceDirectoriesWhichDoNotExist() {
         File srcDir1 = new File(testDir, 'dir1')
         GFileUtils.touch(new File(srcDir1, 'subdir/file1.txt'))
 
+        when:
         set.srcDir 'dir1'
         set.srcDir 'dir2'
 
+        then:
         assertSetContainsForAllTypes(set, 'subdir/file1.txt')
     }
 
-    @Test
     public void failsWhenSourceDirectoryIsNotADirectory() {
         File srcDir = new File(testDir, 'dir1')
         GFileUtils.touch(srcDir)
 
+        when:
         set.srcDir 'dir1'
-        try {
-            set.addToAntBuilder("node", "fileset")
-            fail()
-        } catch (InvalidUserDataException e) {
-            assertThat(e.message, equalTo("Source directory '$srcDir' is not a directory." as String))
-        }
+        set.addToAntBuilder("node", "fileset")
+
+        then:
+        InvalidUserDataException e = thrown()
+        e.message == "Source directory '$srcDir' is not a directory."
     }
 
-    @Test
     public void throwsStopExceptionWhenNoSourceDirectoriesExist() {
+        when:
         set.srcDir 'dir1'
         set.srcDir 'dir2'
+        set.stopExecutionIfEmpty()
 
-        try {
-            set.stopExecutionIfEmpty()
-            fail()
-        } catch (StopExecutionException e) {
-            assertThat(e.message, equalTo('<display-name> does not contain any files.'))
-        }
+        then:
+        StopExecutionException e = thrown()
+        e.message == '<display-name> does not contain any files.'
     }
 
-    @Test
     public void throwsStopExceptionWhenNoSourceDirectoryHasMatches() {
+        when:
         set.srcDir 'dir1'
         File srcDir = new File(testDir, 'dir1')
         srcDir.mkdirs()
+        set.stopExecutionIfEmpty()
 
-        try {
-            set.stopExecutionIfEmpty()
-            fail()
-        } catch (StopExecutionException e) {
-            assertThat(e.message, equalTo('<display-name> does not contain any files.'))
-        }
+        then:
+        StopExecutionException e = thrown()
+        e.message == '<display-name> does not contain any files.'
     }
 
-    @Test
     public void doesNotThrowStopExceptionWhenSomeSourceDirectoriesAreNotEmpty() {
+        when:
         set.srcDir 'dir1'
         GFileUtils.touch(new File(testDir, 'dir1/file1.txt'))
         set.srcDir 'dir2'
-
         set.stopExecutionIfEmpty()
+
+        then:
+        notThrown(Throwable)
     }
 
-    @Test
     public void canUseMatchingMethodToFilterCertainFiles() {
         File srcDir1 = new File(testDir, 'dir1')
         GFileUtils.touch(new File(srcDir1, 'subdir/file1.txt'))
         GFileUtils.touch(new File(srcDir1, 'subdir/file2.txt'))
         GFileUtils.touch(new File(srcDir1, 'subdir2/file1.txt'))
 
+        when:
         set.srcDir 'dir1'
-
         FileTree filteredSet = set.matching {
             include '**/file1.txt'
             exclude 'subdir2/**'
         }
 
+        then:
         assertSetContainsForAllTypes(filteredSet, 'subdir/file1.txt')
     }
 
-    @Test
     public void canUsePatternsAndFilterPatternsAndMatchingMethodToFilterSourceFiles() {
         File srcDir1 = new File(testDir, 'dir1')
         GFileUtils.touch(new File(srcDir1, 'subdir/file1.txt'))
@@ -205,19 +264,19 @@ public class DefaultSourceDirectorySetTest {
         GFileUtils.touch(new File(srcDir1, 'subdir/ignored.txt'))
         GFileUtils.touch(new File(srcDir1, 'subdir2/file1.txt'))
 
+        when:
         set.srcDir 'dir1'
         set.include '**/*file?.*'
         set.filter.include '**/*.txt'
-
         FileTree filteredSet = set.matching {
             include 'subdir/**'
             exclude '**/file2.txt'
         }
 
+        then:
         assertSetContainsForAllTypes(filteredSet, 'subdir/file1.txt')
     }
 
-    @Test
     public void filteredSetIsLive() {
         File srcDir1 = new File(testDir, 'dir1')
         GFileUtils.touch(new File(srcDir1, 'subdir/file1.txt'))
@@ -225,13 +284,17 @@ public class DefaultSourceDirectorySetTest {
         File srcDir2 = new File(testDir, 'dir2')
         GFileUtils.touch(new File(srcDir2, 'subdir2/file1.txt'))
 
+        when:
         set.srcDir 'dir1'
-
         FileTree filteredSet = set.matching { include '**/file1.txt' }
+
+        then:
         assertSetContainsForAllTypes(filteredSet, 'subdir/file1.txt')
 
+        when:
         set.srcDir 'dir2'
 
+        then:
         assertSetContainsForAllTypes(filteredSet, 'subdir/file1.txt', 'subdir2/file1.txt')
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/FileSetTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/FileSetTest.groovy
deleted file mode 100644
index ea69d4e..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/FileSetTest.groovy
+++ /dev/null
@@ -1,304 +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.internal.file
-
-import org.gradle.api.InvalidUserDataException
-import org.gradle.api.Task
-import org.gradle.api.file.FileTree
-import org.gradle.api.internal.tasks.TaskResolver
-import org.gradle.api.tasks.StopExecutionException
-import org.gradle.api.tasks.util.AbstractTestForPatternSet
-import org.gradle.api.tasks.util.PatternFilterable
-import org.gradle.api.tasks.util.PatternSet
-import org.gradle.util.JUnit4GroovyMockery
-import org.gradle.util.TemporaryFolder
-import org.gradle.util.WrapUtil
-import org.jmock.integration.junit4.JUnit4Mockery
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import static org.gradle.api.file.FileVisitorUtil.assertCanStopVisiting
-import static org.gradle.api.file.FileVisitorUtil.assertVisits
-import static org.gradle.api.tasks.AntBuilderAwareUtil.assertSetContainsForAllTypes
-import static org.gradle.util.Matchers.isEmpty
-import static org.hamcrest.Matchers.equalTo
-import static org.hamcrest.Matchers.instanceOf
-import static org.junit.Assert.*
-
-/**
- * @author Hans Dockter
- */
-class FileSetTest extends AbstractTestForPatternSet {
-    JUnit4Mockery context = new JUnit4GroovyMockery();
-    TaskResolver taskResolverStub = context.mock(TaskResolver.class);
-    DefaultConfigurableFileTree fileSet
-    FileResolver fileResolverStub = [resolve: {it as File}] as FileResolver
-    @Rule public TemporaryFolder tmpDir = new TemporaryFolder();
-    File testDir = tmpDir.dir
-
-    PatternFilterable getPatternSet() {
-        return fileSet
-    }
-
-    Class getPatternSetType() {
-        DefaultConfigurableFileTree
-    }
-
-    @Before public void setUp() {
-        super.setUp()
-        fileSet = patternSetType.newInstance(testDir, fileResolverStub, taskResolverStub)
-    }
-
-    @Test public void testFileSetConstructionWithBaseDir() {
-        fileSet = new DefaultConfigurableFileTree(testDir, fileResolverStub, taskResolverStub)
-        assertEquals(testDir, fileSet.dir)
-    }
-
-    @Test public void testFileSetConstructionFromMap() {
-        fileSet = new DefaultConfigurableFileTree(fileResolverStub, taskResolverStub, dir: testDir, includes: ['include'])
-        assertEquals(testDir, fileSet.dir)
-        assertEquals(['include'] as Set, fileSet.includes)
-    }
-
-    @Test(expected = InvalidUserDataException) public void testFileSetConstructionWithNoBaseDirSpecified() {
-        DefaultConfigurableFileTree fileSet = new DefaultConfigurableFileTree([:], fileResolverStub, taskResolverStub)
-        fileSet.matching {}
-    }
-
-    @Test public void testFileSetConstructionWithBaseDirAsString() {
-        DefaultConfigurableFileTree fileSet = new DefaultConfigurableFileTree(fileResolverStub, taskResolverStub, dir: 'dirname')
-        assertEquals(new File('dirname'), fileSet.dir);
-    }
-
-    @Test public void testCanScanForFiles() {
-        File included1 = new File(testDir, 'subDir/included1')
-        File included2 = new File(testDir, 'subDir2/included2')
-        [included1, included2].each {File file ->
-            file.parentFile.mkdirs()
-            file.text = 'some text'
-        }
-
-        assertThat(fileSet.files, equalTo([included1, included2] as Set))
-    }
-
-    @Test public void testCanVisitFiles() {
-        File included1 = new File(testDir, 'subDir/included1')
-        File included2 = new File(testDir, 'subDir2/included2')
-        [included1, included2].each {File file ->
-            file.parentFile.mkdirs()
-            file.text = 'some text'
-        }
-
-        assertVisits(fileSet, ['subDir/included1', 'subDir2/included2'], ['subDir', 'subDir2'])
-    }
-
-    @Test public void testCanStopVisitingFiles() {
-        File included1 = new File(testDir, 'subDir/included1')
-        File included2 = new File(testDir, 'subDir/otherDir/included2')
-        [included1, included2].each {File file ->
-            file.parentFile.mkdirs()
-            file.text = 'some text'
-        }
-
-        assertCanStopVisiting(fileSet)
-    }
-
-    @Test public void testContainsFiles() {
-        File included1 = new File(testDir, 'subDir/included1')
-        File included2 = new File(testDir, 'subDir2/included2')
-        [included1, included2].each {File file ->
-            file.parentFile.mkdirs()
-            file.text = 'some text'
-        }
-
-        assertTrue(fileSet.contains(included1))
-        assertTrue(fileSet.contains(included2))
-        assertFalse(fileSet.contains(testDir))
-        assertFalse(fileSet.contains(included1.parentFile))
-        assertFalse(fileSet.contains(included2.parentFile))
-        assertFalse(fileSet.contains(new File(testDir, 'does not exist')))
-        assertFalse(fileSet.contains(testDir.parentFile))
-        assertFalse(fileSet.contains(new File('something')))
-    }
-
-    @Test public void testCanAddToAntTask() {
-        File included1 = new File(testDir, 'subDir/included1')
-        File included2 = new File(testDir, 'subDir2/included2')
-        [included1, included2].each {File file ->
-            file.parentFile.mkdirs()
-            file.text = 'some text'
-        }
-
-        assertSetContainsForAllTypes(fileSet, 'subDir/included1', 'subDir2/included2')
-    }
-
-    @Test public void testIsEmptyWhenBaseDirDoesNotExist() {
-        fileSet.dir = new File(testDir, 'does not exist')
-
-        assertThat(fileSet.files, isEmpty())
-        assertSetContainsForAllTypes(fileSet)
-        assertVisits(fileSet, [], [])
-    }
-
-    @Test public void testCanSelectFilesUsingPatterns() {
-        File included1 = new File(testDir, 'subDir/included1')
-        File included2 = new File(testDir, 'subDir2/included2')
-        File excluded1 = new File(testDir, 'subDir/notincluded')
-        File ignored1 = new File(testDir, 'ignored')
-        [included1, included2, excluded1, ignored1].each {File file ->
-            file.parentFile.mkdirs()
-            file.text = 'some text'
-        }
-
-        fileSet.include('*/*included*')
-        fileSet.exclude('**/not*')
-
-        assertThat(fileSet.files, equalTo([included1, included2] as Set))
-        assertSetContainsForAllTypes(fileSet, 'subDir/included1', 'subDir2/included2')
-        assertVisits(fileSet, ['subDir/included1', 'subDir2/included2'], ['subDir', 'subDir2'])
-        assertTrue(fileSet.contains(included1))
-        assertFalse(fileSet.contains(excluded1))
-        assertFalse(fileSet.contains(ignored1))
-    }
-
-    @Test public void testCanFilterMatchingFilesUsingConfigureClosure() {
-        File included1 = new File(testDir, 'subDir/included1')
-        File included2 = new File(testDir, 'subDir2/included2')
-        File excluded1 = new File(testDir, 'subDir/notincluded')
-        File ignored1 = new File(testDir, 'ignored')
-        [included1, included2, excluded1, ignored1].each {File file ->
-            file.parentFile.mkdirs()
-            file.text = 'some text'
-        }
-
-        DefaultConfigurableFileTree filtered = fileSet.matching {
-            include('*/*included*')
-            exclude('**/not*')
-        }
-
-        assertThat(filtered.files, equalTo([included1, included2] as Set))
-        assertSetContainsForAllTypes(filtered, 'subDir/included1', 'subDir2/included2')
-        assertVisits(filtered, ['subDir/included1', 'subDir2/included2'], ['subDir', 'subDir2'])
-        assertTrue(filtered.contains(included1))
-        assertFalse(filtered.contains(excluded1))
-        assertFalse(filtered.contains(ignored1))
-    }
-
-    @Test public void testCanFilterMatchingFilesUsingPatternSet() {
-        File included1 = new File(testDir, 'subDir/included1')
-        File included2 = new File(testDir, 'subDir2/included2')
-        File excluded1 = new File(testDir, 'subDir/notincluded')
-        File ignored1 = new File(testDir, 'ignored')
-        [included1, included2, excluded1, ignored1].each {File file ->
-            file.parentFile.mkdirs()
-            file.text = 'some text'
-        }
-
-        PatternSet patternSet = new PatternSet(includes: ['*/*included*'], excludes: ['**/not*'])
-        DefaultConfigurableFileTree filtered = fileSet.matching(patternSet)
-
-        assertThat(filtered.files, equalTo([included1, included2] as Set))
-        assertSetContainsForAllTypes(filtered, 'subDir/included1', 'subDir2/included2')
-        assertVisits(filtered, ['subDir/included1', 'subDir2/included2'], ['subDir', 'subDir2'])
-        assertTrue(filtered.contains(included1))
-        assertFalse(filtered.contains(excluded1))
-        assertFalse(filtered.contains(ignored1))
-    }
-
-    @Test public void testCanFilterAndSelectFiles() {
-        File included1 = new File(testDir, 'subDir/included1')
-        File included2 = new File(testDir, 'subDir2/included2')
-        File excluded1 = new File(testDir, 'subDir/notincluded')
-        File excluded2 = new File(testDir, 'subDir/excluded')
-        File ignored1 = new File(testDir, 'ignored')
-        [included1, included2, excluded1, excluded2, ignored1].each {File file ->
-            file.parentFile.mkdirs()
-            file.text = 'some text'
-        }
-
-        fileSet.exclude '**/excluded*'
-
-        DefaultConfigurableFileTree filtered = fileSet.matching {
-            include('*/*included*')
-            exclude('**/not*')
-        }
-
-        assertThat(filtered.files, equalTo([included1, included2] as Set))
-        assertSetContainsForAllTypes(filtered, 'subDir/included1', 'subDir2/included2')
-        assertVisits(filtered, ['subDir/included1', 'subDir2/included2'], ['subDir', 'subDir2'])
-        assertTrue(filtered.contains(included1))
-        assertFalse(filtered.contains(excluded1))
-        assertFalse(filtered.contains(ignored1))
-    }
-
-    @Test public void testCanAddFileSetsTogether() {
-        FileTree other = new DefaultConfigurableFileTree(testDir, fileResolverStub, taskResolverStub)
-        FileTree sum = fileSet + other
-        assertThat(sum, instanceOf(UnionFileTree))
-        assertThat(sum.sourceCollections, equalTo([fileSet, other]))
-    }
-
-    @Test public void testDisplayName() {
-        assertThat(fileSet.displayName, equalTo("file set '$testDir'".toString()))
-    }
-
-    @Test public void testStopExecutionIfEmptyWhenNoMatchingFilesFound() {
-        fileSet.include('**/*included')
-        new File(testDir, 'excluded').text = 'some text'
-
-        try {
-            fileSet.stopExecutionIfEmpty()
-            fail()
-        } catch (StopExecutionException e) {
-            assertThat(e.message, equalTo("File set '$testDir' does not contain any files." as String))
-        }
-    }
-
-    @Test public void testStopExecutionIfEmptyWhenMatchingFilesFound() {
-        fileSet.include('**/*included')
-        new File(testDir, 'included').text = 'some text'
-
-        fileSet.stopExecutionIfEmpty()
-    }
-
-    @Test
-    public void canGetAndSetTaskDependencies() {
-        FileResolver fileResolverStub = context.mock(FileResolver.class);
-        fileSet = patternSetType.newInstance(testDir, fileResolverStub, taskResolverStub)
-        
-        assertThat(fileSet.getBuiltBy(), isEmpty());
-
-        fileSet.builtBy("a");
-        fileSet.builtBy("b");
-        fileSet.from("f");
-
-        assertThat(fileSet.getBuiltBy(), equalTo(WrapUtil.toSet((Object) "a", "b")));
-
-        fileSet.setBuiltBy(WrapUtil.toList("c"));
-
-        assertThat(fileSet.getBuiltBy(), equalTo(WrapUtil.toSet((Object) "c")));
-        final Task task = context.mock(Task.class);
-        context.checking {
-            allowing(fileResolverStub).resolve("f");
-            will(returnValue(new File("f")));
-            allowing(taskResolverStub).resolveTask('c');
-            will(returnValue(task));
-        }
-
-        assertThat(fileSet.getBuildDependencies().getDependencies(null), equalTo((Set) WrapUtil.toSet(task)));
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/MapFileTreeTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/MapFileTreeTest.java
deleted file mode 100644
index 62f076d..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/MapFileTreeTest.java
+++ /dev/null
@@ -1,75 +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.file;
-
-import groovy.lang.Closure;
-import static org.gradle.api.file.FileVisitorUtil.*;
-import static org.gradle.api.tasks.AntBuilderAwareUtil.*;
-import org.gradle.util.TestFile;
-import org.gradle.util.HelperUtil;
-import org.gradle.util.TemporaryFolder;
-import static org.gradle.util.WrapUtil.*;
-import static org.hamcrest.Matchers.*;
-
-import org.junit.Rule;
-import org.junit.Test;
-
-import java.util.List;
-
-public class MapFileTreeTest {
-    @Rule
-    public final TemporaryFolder tmpDir = new TemporaryFolder();
-    private TestFile rootDir = tmpDir.getDir();
-    private final MapFileTree tree = new MapFileTree(rootDir);
-
-    @Test
-    public void isEmptyByDefault() {
-        List<String> emptyList = toList();
-        assertVisits(tree, emptyList, emptyList);
-        assertSetContainsForAllTypes(tree, emptyList);
-    }
-    
-    @Test
-    public void canAddAnElementUsingAClosureToGeneratedContent() {
-        Closure closure = HelperUtil.toClosure("{it.write('content'.getBytes())}");
-        tree.add("path/file.txt", closure);
-
-        assertVisits(tree, toList("path/file.txt"), toList("path"));
-        assertSetContainsForAllTypes(tree, toList("path/file.txt"));
-
-        rootDir.file("path").assertIsDir();
-        rootDir.file("path/file.txt").assertContents(equalTo("content"));
-    }
-
-    @Test
-    public void canAddMultipleElementsInDifferentDirs() {
-        Closure closure = HelperUtil.toClosure("{it.write('content'.getBytes())}");
-        tree.add("path/file.txt", closure);
-        tree.add("file.txt", closure);
-        tree.add("path/subdir/file.txt", closure);
-
-        assertVisits(tree, toList("path/file.txt", "file.txt", "path/subdir/file.txt"), toList("path", "path/subdir"));
-        assertSetContainsForAllTypes(tree, toList("path/file.txt", "file.txt", "path/subdir/file.txt"));
-    }
-
-    @Test
-    public void canStopVisitingElements() {
-        Closure closure = HelperUtil.toClosure("{it.write('content'.getBytes())}");
-        tree.add("path/file.txt", closure);
-        tree.add("file.txt", closure);
-        assertCanStopVisiting(tree);
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/PathResolvingFileCollectionTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/PathResolvingFileCollectionTest.java
deleted file mode 100644
index be25b98..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/PathResolvingFileCollectionTest.java
+++ /dev/null
@@ -1,351 +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.file;
-
-import groovy.lang.Closure;
-import org.gradle.api.Task;
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.tasks.TaskResolver;
-import org.gradle.api.tasks.TaskDependency;
-import org.gradle.util.GFileUtils;
-import org.gradle.util.HelperUtil;
-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.util.List;
-import java.util.Set;
-import java.util.concurrent.Callable;
-
-import static org.gradle.util.Matchers.*;
-import static org.gradle.util.WrapUtil.*;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
- at RunWith(JMock.class)
-public class PathResolvingFileCollectionTest {
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    @Rule
-    public TemporaryFolder tmpDir = new TemporaryFolder();
-    private final File testDir = tmpDir.getDir();
-    private final FileResolver resolverMock = context.mock(FileResolver.class);
-    private final TaskResolver taskResolverStub = context.mock(TaskResolver.class);
-    private final PathResolvingFileCollection collection = new PathResolvingFileCollection(resolverMock,
-            taskResolverStub);
-
-    @Test
-    public void resolvesSpecifiedFilesUseFileResolver() {
-        final File file1 = new File("1");
-        final File file2 = new File("2");
-
-        PathResolvingFileCollection collection = new PathResolvingFileCollection(resolverMock, taskResolverStub, file1,
-                file2);
-
-        context.checking(new Expectations() {{
-            one(resolverMock).resolve(file1);
-            will(returnValue(file1));
-            one(resolverMock).resolve(file2);
-            will(returnValue(file2));
-        }});
-
-        assertThat(collection.getFiles(), equalTo(toLinkedSet(file1, file2)));
-    }
-
-    @Test
-    public void canAddPathsToTheCollection() {
-        final File file1 = new File("1");
-        final File file2 = new File("2");
-
-        collection.from("src1", "src2");
-
-        context.checking(new Expectations() {{
-            one(resolverMock).resolve("src1");
-            will(returnValue(file1));
-            one(resolverMock).resolve("src2");
-            will(returnValue(file2));
-        }});
-
-        assertThat(collection.getFiles(), equalTo(toLinkedSet(file1, file2)));
-    }
-
-    @Test
-    public void resolvesSpecifiedPathsUseFileResolver() {
-        final File file1 = new File("1");
-        final File file2 = new File("2");
-
-        PathResolvingFileCollection collection = new PathResolvingFileCollection(resolverMock, taskResolverStub, "src1",
-                "src2");
-
-        context.checking(new Expectations() {{
-            one(resolverMock).resolve("src1");
-            will(returnValue(file1));
-            one(resolverMock).resolve("src2");
-            will(returnValue(file2));
-        }});
-
-        assertThat(collection.getFiles(), equalTo(toLinkedSet(file1, file2)));
-    }
-
-    @Test
-    public void canUseAClosureToSpecifyTheContentsOfTheCollection() {
-        final File file1 = new File("1");
-        final File file2 = new File("2");
-
-        context.checking(new Expectations() {{
-            allowing(resolverMock).resolve('a');
-            will(returnValue(file1));
-            allowing(resolverMock).resolve('b');
-            will(returnValue(file2));
-        }});
-
-        List<Character> files = toList('a');
-        Closure closure = HelperUtil.returns(files);
-        collection.from(closure);
-
-        assertThat(collection.getFiles(), equalTo(toLinkedSet(file1)));
-
-        files.add('b');
-
-        assertThat(collection.getFiles(), equalTo(toLinkedSet(file1, file2)));
-    }
-
-    @Test
-    public void canUseAClosureToSpecifyASingleFile() {
-        Closure closure = HelperUtil.returns('a');
-        final File file = new File("1");
-
-        collection.from(closure);
-
-        context.checking(new Expectations() {{
-            one(resolverMock).resolve('a');
-            will(returnValue(file));
-        }});
-
-        assertThat(collection.getFiles(), equalTo(toLinkedSet(file)));
-    }
-
-    @Test
-    public void closureCanReturnNull() {
-        Closure closure = HelperUtil.returns(null);
-
-        collection.from(closure);
-
-        assertThat(collection.getFiles(), isEmpty());
-    }
-
-    @Test
-    public void canUseACollectionToSpecifyTheContentsOfTheCollection() {
-        final File file1 = new File("1");
-        final File file2 = new File("2");
-
-        context.checking(new Expectations() {{
-            allowing(resolverMock).resolve("src1");
-            will(returnValue(file1));
-            allowing(resolverMock).resolve("src2");
-            will(returnValue(file2));
-        }});
-
-        List<String> files = toList("src1");
-        collection.from(files);
-
-        assertThat(collection.getFiles(), equalTo(toLinkedSet(file1)));
-
-        files.add("src2");
-
-        assertThat(collection.getFiles(), equalTo(toLinkedSet(file1, file2)));
-    }
-
-    @Test
-    public void canUseAnArrayToSpecifyTheContentsOfTheCollection() {
-        final File file1 = new File("1");
-        final File file2 = new File("2");
-
-        context.checking(new Expectations() {{
-            allowing(resolverMock).resolve("src1");
-            will(returnValue(file1));
-            allowing(resolverMock).resolve("src2");
-            will(returnValue(file2));
-        }});
-
-        collection.from((Object) toArray("src1", "src2"));
-        assertThat(collection.getFiles(), equalTo(toLinkedSet(file1, file2)));
-    }
-
-    @Test
-    public void canUseNestedObjectsToSpecifyTheContentsOfTheCollection() {
-        final File file1 = new File("1");
-        final File file2 = new File("2");
-
-        context.checking(new Expectations() {{
-            allowing(resolverMock).resolve("src1");
-            will(returnValue(file1));
-            allowing(resolverMock).resolve("src2");
-            will(returnValue(file2));
-        }});
-
-        collection.from(HelperUtil.toClosure("{[{['src1', { ['src2'] as String[] }]}]}"));
-        assertThat(collection.getFiles(), equalTo(toLinkedSet(file1, file2)));
-    }
-
-    @Test
-    public void canUseAFileCollectionToSpecifyTheContentsOfTheCollection() {
-        final File file1 = new File("1");
-        final File file2 = new File("2");
-
-        final FileCollection src = context.mock(FileCollection.class);
-
-        collection.from(src);
-
-        context.checking(new Expectations() {{
-            one(src).getFiles();
-            will(returnValue(toLinkedSet(file1)));
-        }});
-
-        assertThat(collection.getFiles(), equalTo(toLinkedSet(file1)));
-
-        context.checking(new Expectations() {{
-            one(src).getFiles();
-            will(returnValue(toLinkedSet(file1, file2)));
-        }});
-
-        assertThat(collection.getFiles(), equalTo(toLinkedSet(file1, file2)));
-    }
-
-    @Test
-    public void canUseACallableToSpecifyTheContentsOfTheCollection() throws Exception {
-        final File file1 = new File("1");
-        final File file2 = new File("2");
-        final Callable callable = context.mock(Callable.class);
-
-        context.checking(new Expectations() {{
-            one(callable).call();
-            will(returnValue(toList("src1", "src2")));
-            allowing(resolverMock).resolve("src1");
-            will(returnValue(file1));
-            allowing(resolverMock).resolve("src2");
-            will(returnValue(file2));
-        }});
-
-        collection.from(callable);
-        assertThat(collection.getFiles(), equalTo(toLinkedSet(file1, file2)));
-    }
-
-    @Test
-    public void callableCanReturnNull() throws Exception {
-        final Callable callable = context.mock(Callable.class);
-
-        context.checking(new Expectations() {{
-            one(callable).call();
-            will(returnValue(null));
-        }});
-
-        collection.from(callable);
-        assertThat(collection.getFiles(), isEmpty());
-    }
-
-    @Test
-    public void treatsEachFileAsASingletonFileCollection() {
-        final File file = new File(testDir, "f");
-        GFileUtils.touch(file);
-
-        context.checking(new Expectations() {{
-            one(resolverMock).resolve("file");
-            will(returnValue(file));
-        }});
-
-        collection.from("file");
-        assertThat(collection.getSourceCollections().get(0), instanceOf(SingletonFileCollection.class));
-    }
-
-    @Test
-    public void treatsEachFileCollectionAsFileCollection() {
-        final FileCollection fileCollectionMock = context.mock(FileCollection.class);
-
-        collection.from(fileCollectionMock);
-        assertThat((Iterable<FileCollection>) collection.getSourceCollections(), hasItem(fileCollectionMock));
-    }
-
-    @Test
-    public void canGetAndSetTaskDependencies() {
-        assertThat(collection.getBuiltBy(), isEmpty());
-
-        collection.builtBy("a");
-        collection.builtBy("b");
-        collection.from("f");
-
-        assertThat(collection.getBuiltBy(), equalTo(toSet((Object) "a", "b")));
-
-        collection.setBuiltBy(toList("c"));
-
-        assertThat(collection.getBuiltBy(), equalTo(toSet((Object) "c")));
-
-        final Task task = context.mock(Task.class);
-        context.checking(new Expectations() {{
-            allowing(resolverMock).resolve("f");
-            will(returnValue(new File("f")));
-            allowing(taskResolverStub).resolveTask("c");
-            will(returnValue(task));
-        }});
-
-        assertThat(collection.getBuildDependencies().getDependencies(null), equalTo((Set) toSet(task)));
-    }
-
-    @Test
-    public void taskDependenciesContainsUnionOfDependenciesOfNestedFileCollectionsPlusOwnDependencies() {
-        final FileCollection fileCollectionMock = context.mock(FileCollection.class);
-
-        collection.from(fileCollectionMock);
-        collection.from("f");
-        collection.builtBy('b');
-
-        final Task taskA = context.mock(Task.class, "a");
-        final Task taskB = context.mock(Task.class, "b");
-        context.checking(new Expectations() {{
-            allowing(resolverMock).resolve("f");
-            will(returnValue(new File("f")));
-            TaskDependency dependency = context.mock(TaskDependency.class);
-            allowing(fileCollectionMock).getBuildDependencies();
-            will(returnValue(dependency));
-            allowing(dependency).getDependencies(null);
-            will(returnValue(toSet(taskA)));
-            allowing(taskResolverStub).resolveTask('b');
-            will(returnValue(taskB));
-        }});
-
-        assertThat(collection.getBuildDependencies().getDependencies(null), equalTo((Set) toSet(taskA, taskB)));
-    }
-
-    @Test
-    public void hasSpecifiedDependenciesWhenEmpty() {
-        collection.builtBy("task");
-
-        final Task task = context.mock(Task.class);
-        context.checking(new Expectations(){{
-            allowing(taskResolverStub).resolveTask("task");
-            will(returnValue(task));
-        }});
-
-        assertThat(collection.getBuildDependencies().getDependencies(null), equalTo((Set) toSet(task)));
-        assertThat(collection.getAsFileTree().getBuildDependencies().getDependencies(null), equalTo((Set) toSet(task)));
-        assertThat(collection.getAsFileTree().matching(HelperUtil.TEST_CLOSURE).getBuildDependencies().getDependencies(null), equalTo((Set) toSet(task)));
-    }
-    
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/SimpleFileCollectionTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/SimpleFileCollectionTest.groovy
deleted file mode 100644
index dcb300d..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/SimpleFileCollectionTest.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.api.internal.file
-
-import spock.lang.Specification
-
-class SimpleFileCollectionTest extends Specification {
-    def collectionContainsFixedSetOfFiles() {
-        File file1 = new File('file1')
-        File file2 = new File('file2')
-
-        expect:
-        SimpleFileCollection collection = new SimpleFileCollection(file1, file2)
-        collection.files as List == [file1, file2]
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/SingletonFileCollectionTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/SingletonFileCollectionTest.groovy
deleted file mode 100644
index 8ba1703..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/SingletonFileCollectionTest.groovy
+++ /dev/null
@@ -1,41 +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.file
-
-import org.gradle.api.tasks.TaskDependency
-import org.junit.Test
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
-
-class SingletonFileCollectionTest {
-    private final File testFile = new File('test-file')
-    private final SingletonFileCollection collection = new SingletonFileCollection(testFile, [:] as TaskDependency)
-
-    @Test
-    public void hasUsefulDisplayName() {
-        assertThat(collection.displayName, equalTo("file '$testFile'".toString()));
-    }
-
-    @Test
-    public void containsASingleFile() {
-        assertThat(collection.files, equalTo([testFile] as Set))
-    }
-
-    @Test
-    public void convertsSelfToSingletonFileTree() {
-        assertThat(collection.asFileTree, instanceOf(SingletonFileTree))
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/SingletonFileTreeTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/SingletonFileTreeTest.groovy
deleted file mode 100644
index f94824d..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/SingletonFileTreeTest.groovy
+++ /dev/null
@@ -1,162 +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.file
-
-import org.gradle.api.file.FileTree
-import org.gradle.api.tasks.TaskDependency
-import org.gradle.util.TestFile
-import org.gradle.util.TemporaryFolder
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import static org.gradle.api.file.FileVisitorUtil.*
-import static org.gradle.api.tasks.AntBuilderAwareUtil.*
-import static org.gradle.util.Matchers.*
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
-import org.gradle.api.Task
-
-class SingletonFileTreeTest {
-    @Rule public TemporaryFolder rootDir = new TemporaryFolder();
-    private final TestFile testFile = rootDir.dir.file('test.txt')
-    private final TestFile missingFile = rootDir.dir.file('missing')
-    private final TestFile testDir = rootDir.dir.file('test-dir')
-    private final TaskDependency dependency = [:] as TaskDependency
-
-    @Before
-    public void setUp() {
-        testFile.touch()
-        testDir.create {
-            subdir1 {
-                file 'file1.txt'
-                file 'file2.txt'
-            }
-            subdir2 {
-                file 'file1.txt'
-            }
-        }
-    }
-
-    @Test
-    public void hasUsefulDisplayName() {
-        SingletonFileTree tree = new SingletonFileTree(testFile, dependency)
-        assertThat(tree.displayName, equalTo("file '$testFile'".toString()));
-    }
-
-    @Test
-    public void containsASingleFileWhenSourceIsAFile() {
-        SingletonFileTree tree = new SingletonFileTree(testFile, dependency)
-        assertThat(tree.files, equalTo([testFile] as Set))
-    }
-
-    @Test
-    public void containsDescendentFilesWhenSourceIsADirectory() {
-        SingletonFileTree tree = new SingletonFileTree(testDir, dependency)
-        assertThat(tree.files, equalTo(testDir.files('subdir1/file1.txt', 'subdir1/file2.txt', 'subdir2/file1.txt') as Set))
-    }
-
-    @Test
-    public void isEmptyWhenSourceDoesNotExist() {
-        SingletonFileTree tree = new SingletonFileTree(missingFile, dependency)
-        assertThat(tree.files, isEmpty())
-    }
-
-    @Test
-    public void addsToAntBuilderWhenSourceIsAFile() {
-        SingletonFileTree tree = new SingletonFileTree(testFile, dependency)
-        assertSetContains(tree, 'test.txt')
-    }
-
-    @Test
-    public void addsToAntBuilderWhenSourceIsADirectory() {
-        SingletonFileTree tree = new SingletonFileTree(testDir, dependency)
-        assertSetContains(tree, 'subdir1/file1.txt', 'subdir1/file2.txt', 'subdir2/file1.txt')
-    }
-
-    @Test
-    public void addsToAntBuilderWhenSourceDoesNotExist() {
-        SingletonFileTree tree = new SingletonFileTree(missingFile, dependency)
-        assertSetContains(tree)
-    }
-
-    @Test
-    public void visitsASingleFileWhenSourceIsAFile() {
-        SingletonFileTree tree = new SingletonFileTree(testFile, dependency)
-        assertVisits(tree, ['test.txt'], [])
-    }
-
-    @Test
-    public void visitsDescendentFilesWhenSourceIsADirectory() {
-        SingletonFileTree tree = new SingletonFileTree(testDir, dependency)
-        assertVisits(tree, ['subdir1/file1.txt', 'subdir1/file2.txt', 'subdir2/file1.txt'], ['subdir1', 'subdir2'])
-    }
-
-    @Test
-    public void visitsNothingWhenSourceDoesNotExist() {
-        SingletonFileTree tree = new SingletonFileTree(missingFile, dependency)
-        assertVisits(tree, [], [])
-    }
-
-    @Test
-    public void canRestrictContentsWhenSourceIsAFile() {
-        SingletonFileTree tree = new SingletonFileTree(testFile, dependency)
-
-        FileTree filtered = tree.matching { exclude '*.txt' }
-        assertThat(filtered.files, isEmpty())
-        assertSetContains(filtered)
-        assertVisits(filtered, [], [])
-
-        filtered = tree.matching { include '*.txt' }
-        assertThat(filtered.files, equalTo([testFile] as Set))
-        assertSetContains(filtered, 'test.txt')
-        assertVisits(filtered, ['test.txt'], [])
-    }
-
-    @Test
-    public void canRestrictContentsWhenSourceIsADirectory() {
-        SingletonFileTree tree = new SingletonFileTree(testDir, dependency)
-
-        FileTree filtered = tree.matching { exclude '**/file1.txt' }
-        assertThat(filtered.files, equalTo([testDir.file('subdir1/file2.txt')] as Set))
-        assertSetContains(filtered, 'subdir1/file2.txt')
-        assertVisits(filtered, ['subdir1/file2.txt'], ['subdir1', 'subdir2'])
-
-        filtered = tree.matching { include '**/file2.txt' }
-        assertThat(filtered.files, equalTo([testDir.file('subdir1/file2.txt')] as Set))
-        assertSetContains(filtered, 'subdir1/file2.txt')
-        assertVisits(filtered, ['subdir1/file2.txt'], ['subdir1', 'subdir2'])
-    }
-
-    @Test
-    public void canRestrictContentsWhenSourceDoesNotExist() {
-        SingletonFileTree tree = new SingletonFileTree(missingFile, dependency)
-
-        FileTree filtered = tree.matching { exclude '*.txt' }
-        assertThat(filtered.files, isEmpty())
-        assertSetContains(filtered)
-        assertVisits(filtered, [], [])
-    }
-
-    @Test
-    public void hasSpecifiedDependencies() {
-        Task task = [:] as Task
-        SingletonFileTree tree = new SingletonFileTree(missingFile, [getDependencies: { [task] as Set}] as TaskDependency)
-        assertThat(tree.buildDependencies.getDependencies(null), equalTo([task] as Set))
-        assertThat(tree.matching{}.buildDependencies.getDependencies(null), equalTo([task] as Set))
-    }
-}
-
-
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/UnionFileCollectionTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/UnionFileCollectionTest.java
index b8392fb..0143acd 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/UnionFileCollectionTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/UnionFileCollectionTest.java
@@ -16,17 +16,18 @@
 package org.gradle.api.internal.file;
 
 import org.gradle.api.file.FileCollection;
-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.Test;
 import org.junit.runner.RunWith;
 
 import java.io.File;
 
+import static org.gradle.util.WrapUtil.*;
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertThat;
+
 @RunWith(JMock.class)
 public class UnionFileCollectionTest {
     private final JUnit4Mockery context = new JUnit4Mockery();
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/UnionFileTreeTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/UnionFileTreeTest.java
index 2c6aa91..25229e7 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/UnionFileTreeTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/UnionFileTreeTest.java
@@ -17,14 +17,16 @@ package org.gradle.api.internal.file;
 
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.file.FileTree;
-import static org.gradle.util.WrapUtil.*;
-import static org.hamcrest.Matchers.*;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.integration.junit4.JUnit4Mockery;
-import static org.junit.Assert.*;
 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.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
 @RunWith(JMock.class)
 public class UnionFileTreeTest {
     private final JUnit4Mockery context = new JUnit4Mockery();
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/TarFileTreeTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/TarFileTreeTest.java
index 005f923..fe98cad 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/TarFileTreeTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/TarFileTreeTest.java
@@ -73,7 +73,7 @@ public class TarFileTreeTest {
         tarFile.createDir();
 
         try {
-            tree.getFiles();
+            tree.visit(null);
             fail();
         } catch (InvalidUserDataException e) {
             assertThat(e.getMessage(), equalTo("Cannot expand TAR '" + tarFile + "' as it is not a file."));
@@ -86,7 +86,7 @@ public class TarFileTreeTest {
         tarFile.write("not a tar file");
 
         try {
-            tree.getFiles();
+            tree.visit(null);
             fail();
         } catch (GradleException e) {
             assertThat(e.getMessage(), equalTo("Could not expand TAR '" + tarFile + "'."));
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/ZipFileTreeTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/ZipFileTreeTest.java
index 3b3f6ba..473b2da 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/ZipFileTreeTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/ZipFileTreeTest.java
@@ -73,7 +73,7 @@ public class ZipFileTreeTest {
         zipFile.createDir();
 
         try {
-            tree.getFiles();
+            tree.visit(null);
             fail();
         } catch (InvalidUserDataException e) {
             assertThat(e.getMessage(), equalTo("Cannot expand ZIP '" + zipFile + "' as it is not a file."));
@@ -85,7 +85,7 @@ public class ZipFileTreeTest {
         zipFile.write("not a zip file");
 
         try {
-            tree.getFiles();
+            tree.visit(null);
             fail();
         } catch (GradleException e) {
             assertThat(e.getMessage(), equalTo("Could not expand ZIP '" + zipFile + "'."));
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/BuildDependenciesOnlyFileCollectionResolveContextTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/BuildDependenciesOnlyFileCollectionResolveContextTest.groovy
new file mode 100644
index 0000000..4490299
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/BuildDependenciesOnlyFileCollectionResolveContextTest.groovy
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections
+
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.api.tasks.TaskDependency
+import spock.lang.Specification
+import org.gradle.api.file.FileTree
+import org.gradle.api.file.FileCollection
+
+class BuildDependenciesOnlyFileCollectionResolveContextTest extends Specification {
+    final BuildDependenciesOnlyFileCollectionResolveContext context = new BuildDependenciesOnlyFileCollectionResolveContext()
+
+    def resolveAsBuildableReturnsEmptyListWhenContextIsEmpty() {
+        expect:
+        context.resolveAsBuildables() == []
+    }
+
+    def resolveAsBuildableIgnoresAMinimalFileCollection() {
+        MinimalFileCollection fileCollection = Mock()
+
+        when:
+        context.add(fileCollection)
+        def result = context.resolveAsBuildables()
+
+        then:
+        result.size() == 0
+    }
+
+    def resolveAsBuildableWrapsAMinimalFileCollectionWhichImplementsBuildableInAnEmptyFileTree() {
+        TestFileSet fileCollection = Mock()
+        TaskDependency buildDependency = Mock()
+
+        when:
+        context.add(fileCollection)
+        def result = context.resolveAsBuildables()
+
+        then:
+        result.size() == 1
+        result[0] instanceof FileTreeAdapter
+        result[0].tree instanceof EmptyFileTree
+        result[0].tree.buildDependencies == buildDependency
+        1 * fileCollection.buildDependencies >> buildDependency
+    }
+
+    def resolveAsBuildableIgnoresAMinimalFileTree() {
+        MinimalFileTree fileTree = Mock()
+
+        when:
+        context.add(fileTree)
+        def result = context.resolveAsBuildables()
+
+        then:
+        result == []
+    }
+
+    def resolveAsBuildableWrapsAMinimalFileTreeWhichImplementsBuildable() {
+        TestFileTree fileTree = Mock()
+        TaskDependency dependency = Mock()
+
+        when:
+        context.add(fileTree)
+        def result = context.resolveAsBuildables()
+
+        then:
+        result.size() == 1
+        result[0] instanceof FileTreeAdapter
+        result[0].tree instanceof EmptyFileTree
+        result[0].tree.buildDependencies == dependency
+        1 * fileTree.buildDependencies >> dependency
+    }
+
+    def resolveAsBuildablesForAFileCollection() {
+        FileCollection fileCollection = Mock()
+        TaskDependency dependency = Mock()
+
+        when:
+        context.add(fileCollection)
+        def result = context.resolveAsBuildables()
+
+        then:
+        result.size() == 1
+        result[0] instanceof FileTreeAdapter
+        result[0].tree instanceof EmptyFileTree
+        result[0].tree.buildDependencies == dependency
+        1 * fileCollection.buildDependencies >> dependency
+    }
+
+    def resolveAsBuildablesDelegatesToACompositeFileCollection() {
+        FileCollectionContainer composite = Mock()
+        FileTree contents = Mock()
+
+        when:
+        context.add(composite)
+        def result = context.resolveAsBuildables()
+
+        then:
+        result.size() == 1
+        result[0] instanceof FileTreeAdapter
+        result[0].tree instanceof EmptyFileTree
+        1 * composite.resolve(!null) >> { it[0].add(contents) }
+    }
+
+    def resolveAsBuildablesWrapsATaskDependencyInAnEmptyFileTree() {
+        TaskDependency dependency = Mock()
+
+        when:
+        context.add(dependency)
+        def result = context.resolveAsBuildables()
+
+        then:
+        result.size() == 1
+        result[0] instanceof FileTreeAdapter
+        result[0].tree instanceof EmptyFileTree
+        result[0].tree.buildDependencies == dependency
+    }
+
+    def resolveAsBuildablesIgnoresOtherTypes() {
+        when:
+        context.add('a')
+        def result = context.resolveAsBuildables()
+
+        then:
+        result == []
+    }
+
+    def canPushContextWhenResolvingBuildables() {
+        FileResolver fileResolver = Mock()
+        TaskDependency dependency = Mock()
+
+        when:
+        context.push(fileResolver).add(dependency)
+        def result = context.resolveAsBuildables()
+
+        then:
+        result.size() == 1
+        result[0] instanceof FileTreeAdapter
+        result[0].tree instanceof EmptyFileTree
+        result[0].tree.buildDependencies == dependency
+    }
+
+    def pushedContextIgnoresOtherTypes() {
+        FileResolver fileResolver = Mock()
+
+        when:
+        context.push(fileResolver).add('a')
+        def result = context.resolveAsBuildables()
+
+        then:
+        result == []
+    }
+
+    def nestedContextIgnoresOtherTypes() {
+        when:
+        def nested = context.newContext()
+        nested.add('a')
+        def result = nested.resolveAsFileCollections()
+
+        then:
+        result == []
+    }
+
+    def file(String name) {
+        File f = Mock()
+        _ * f.file >> true
+        _ * f.exists() >> true
+        _ * f.canonicalFile >> f
+        f
+    }
+
+    def directory(String name) {
+        File f = Mock()
+        _ * f.directory >> true
+        _ * f.exists() >> true
+        _ * f.canonicalFile >> f
+        f
+    }
+
+    def nonExistent(String name) {
+        File f = Mock()
+        _ * f.canonicalFile >> f
+        f
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/DefaultConfigurableFileCollectionTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/DefaultConfigurableFileCollectionTest.java
new file mode 100644
index 0000000..93bd3da
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/DefaultConfigurableFileCollectionTest.java
@@ -0,0 +1,368 @@
+/*
+ * 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.collections;
+
+import groovy.lang.Closure;
+import org.gradle.api.Task;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.tasks.TaskResolver;
+import org.gradle.api.tasks.TaskDependency;
+import org.gradle.util.HelperUtil;
+import org.gradle.util.JUnit4GroovyMockery;
+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.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+import static org.gradle.util.Matchers.isEmpty;
+import static org.gradle.util.WrapUtil.*;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
+
+ at RunWith(JMock.class)
+public class DefaultConfigurableFileCollectionTest {
+    private final JUnit4Mockery context = new JUnit4GroovyMockery();
+    @Rule
+    public TemporaryFolder tmpDir = new TemporaryFolder();
+    private final FileResolver resolverMock = context.mock(FileResolver.class);
+    private final TaskResolver taskResolverStub = context.mock(TaskResolver.class);
+    private final DefaultConfigurableFileCollection collection = new DefaultConfigurableFileCollection(resolverMock,
+            taskResolverStub);
+
+    @Test
+    public void resolvesSpecifiedFilesUseFileResolver() {
+        final File file1 = new File("1");
+        final File file2 = new File("2");
+
+        DefaultConfigurableFileCollection collection = new DefaultConfigurableFileCollection(resolverMock, taskResolverStub, "a", "b");
+
+        context.checking(new Expectations() {{
+            one(resolverMock).resolve("a");
+            will(returnValue(file1));
+            one(resolverMock).resolve("b");
+            will(returnValue(file2));
+        }});
+
+        assertThat(collection.getFrom(), equalTo(toLinkedSet((Object) "a", "b")));
+        assertThat(collection.getFiles(), equalTo(toLinkedSet(file1, file2)));
+    }
+
+    @Test
+    public void canAddPathsToTheCollection() {
+        collection.from("src1", "src2");
+        assertThat(collection.getFrom(), equalTo(toLinkedSet((Object) "src1", "src2")));
+    }
+
+    @Test
+    public void canSetThePathsOfTheCollection() {
+        collection.from("ignore-me");
+
+        collection.setFrom("src1", "src2");
+        assertThat(collection.getFrom(), equalTo(toLinkedSet((Object) "src1", "src2")));
+
+        collection.setFrom(toList("a", "b"));
+        assertThat(collection.getFrom(), equalTo(toLinkedSet((Object) toList("a", "b"))));
+    }
+
+    @Test
+    public void resolvesSpecifiedPathsUseFileResolver() {
+        final File file1 = new File("1");
+        final File file2 = new File("2");
+
+        DefaultConfigurableFileCollection collection = new DefaultConfigurableFileCollection(resolverMock, taskResolverStub, "src1",
+                "src2");
+
+        context.checking(new Expectations() {{
+            one(resolverMock).resolve("src1");
+            will(returnValue(file1));
+            one(resolverMock).resolve("src2");
+            will(returnValue(file2));
+        }});
+
+        assertThat(collection.getFiles(), equalTo(toLinkedSet(file1, file2)));
+    }
+
+    @Test
+    public void canUseAClosureToSpecifyTheContentsOfTheCollection() {
+        final File file1 = new File("1");
+        final File file2 = new File("2");
+
+        context.checking(new Expectations() {{
+            allowing(resolverMock).resolve('a');
+            will(returnValue(file1));
+            allowing(resolverMock).resolve('b');
+            will(returnValue(file2));
+        }});
+
+        List<Character> files = toList('a');
+        Closure closure = HelperUtil.returns(files);
+        collection.from(closure);
+
+        assertThat(collection.getFiles(), equalTo(toLinkedSet(file1)));
+
+        files.add('b');
+
+        assertThat(collection.getFiles(), equalTo(toLinkedSet(file1, file2)));
+    }
+
+    @Test
+    public void canUseAClosureToSpecifyASingleFile() {
+        Closure closure = HelperUtil.returns('a');
+        final File file = new File("1");
+
+        collection.from(closure);
+
+        context.checking(new Expectations() {{
+            one(resolverMock).resolve('a');
+            will(returnValue(file));
+        }});
+
+        assertThat(collection.getFiles(), equalTo(toLinkedSet(file)));
+    }
+
+    @Test
+    public void closureCanReturnNull() {
+        Closure closure = HelperUtil.returns(null);
+
+        collection.from(closure);
+
+        assertThat(collection.getFiles(), isEmpty());
+    }
+
+    @Test
+    public void canUseACollectionToSpecifyTheContentsOfTheCollection() {
+        final File file1 = new File("1");
+        final File file2 = new File("2");
+
+        context.checking(new Expectations() {{
+            allowing(resolverMock).resolve("src1");
+            will(returnValue(file1));
+            allowing(resolverMock).resolve("src2");
+            will(returnValue(file2));
+        }});
+
+        List<String> files = toList("src1");
+        collection.from(files);
+
+        assertThat(collection.getFiles(), equalTo(toLinkedSet(file1)));
+
+        files.add("src2");
+
+        assertThat(collection.getFiles(), equalTo(toLinkedSet(file1, file2)));
+    }
+
+    @Test
+    public void canUseAnArrayToSpecifyTheContentsOfTheCollection() {
+        final File file1 = new File("1");
+        final File file2 = new File("2");
+
+        context.checking(new Expectations() {{
+            allowing(resolverMock).resolve("src1");
+            will(returnValue(file1));
+            allowing(resolverMock).resolve("src2");
+            will(returnValue(file2));
+        }});
+
+        collection.from((Object) toArray("src1", "src2"));
+        assertThat(collection.getFiles(), equalTo(toLinkedSet(file1, file2)));
+    }
+
+    @Test
+    public void canUseNestedObjectsToSpecifyTheContentsOfTheCollection() {
+        final File file1 = new File("1");
+        final File file2 = new File("2");
+
+        context.checking(new Expectations() {{
+            allowing(resolverMock).resolve("src1");
+            will(returnValue(file1));
+            allowing(resolverMock).resolve("src2");
+            will(returnValue(file2));
+        }});
+
+        collection.from(HelperUtil.toClosure("{[{['src1', { ['src2'] as String[] }]}]}"));
+        assertThat(collection.getFiles(), equalTo(toLinkedSet(file1, file2)));
+    }
+
+    @Test
+    public void canUseAFileCollectionToSpecifyTheContentsOfTheCollection() {
+        final File file1 = new File("1");
+        final File file2 = new File("2");
+
+        final FileCollection src = context.mock(FileCollection.class);
+
+        collection.from(src);
+
+        context.checking(new Expectations() {{
+            one(src).getFiles();
+            will(returnValue(toLinkedSet(file1)));
+        }});
+
+        assertThat(collection.getFiles(), equalTo(toLinkedSet(file1)));
+
+        context.checking(new Expectations() {{
+            one(src).getFiles();
+            will(returnValue(toLinkedSet(file1, file2)));
+        }});
+
+        assertThat(collection.getFiles(), equalTo(toLinkedSet(file1, file2)));
+    }
+
+    @Test
+    public void canUseACallableToSpecifyTheContentsOfTheCollection() throws Exception {
+        final File file1 = new File("1");
+        final File file2 = new File("2");
+        final Callable callable = context.mock(Callable.class);
+
+        context.checking(new Expectations() {{
+            one(callable).call();
+            will(returnValue(toList("src1", "src2")));
+            allowing(resolverMock).resolve("src1");
+            will(returnValue(file1));
+            allowing(resolverMock).resolve("src2");
+            will(returnValue(file2));
+        }});
+
+        collection.from(callable);
+        assertThat(collection.getFiles(), equalTo(toLinkedSet(file1, file2)));
+    }
+
+    @Test
+    public void callableCanReturnNull() throws Exception {
+        final Callable callable = context.mock(Callable.class);
+
+        context.checking(new Expectations() {{
+            one(callable).call();
+            will(returnValue(null));
+        }});
+
+        collection.from(callable);
+        assertThat(collection.getFiles(), isEmpty());
+    }
+
+    @Test
+    public void resolveAddsEachSourceObjectAndBuildDependencies() {
+        final FileCollectionResolveContext resolveContext = context.mock(FileCollectionResolveContext.class);
+        final FileCollectionResolveContext nestedContext = context.mock(FileCollectionResolveContext.class);
+        final FileCollection fileCollectionMock = context.mock(FileCollection.class);
+
+        collection.from("file");
+        collection.from(fileCollectionMock);
+
+        context.checking(new Expectations() {{
+            one(resolveContext).push(resolverMock);
+            will(returnValue(nestedContext));
+            one(nestedContext).add(collection.getFrom());
+        }});
+
+        collection.resolve(resolveContext);
+    }
+
+    @Test
+    public void resolveBuildDependenciesWhenNoEmpty() {
+        final FileCollectionResolveContext resolveContext = context.mock(FileCollectionResolveContext.class);
+        final FileCollectionResolveContext nestedContext = context.mock(FileCollectionResolveContext.class);
+        final FileCollection fileCollectionMock = context.mock(FileCollection.class);
+
+        collection.from("file");
+        collection.builtBy("classes");
+        collection.from(fileCollectionMock);
+
+        context.checking(new Expectations() {{
+            one(resolveContext).push(resolverMock);
+            will(returnValue(nestedContext));
+            one(nestedContext).add(with(notNullValue(TaskDependency.class)));
+            one(nestedContext).add(collection.getFrom());
+        }});
+
+        collection.resolve(resolveContext);
+    }
+
+    @Test
+    public void canGetAndSetTaskDependencies() {
+        assertThat(collection.getBuiltBy(), isEmpty());
+
+        collection.builtBy("a");
+        collection.builtBy("b");
+        collection.from("f");
+
+        assertThat(collection.getBuiltBy(), equalTo(toSet((Object) "a", "b")));
+
+        collection.setBuiltBy(toList("c"));
+
+        assertThat(collection.getBuiltBy(), equalTo(toSet((Object) "c")));
+
+        final Task task = context.mock(Task.class);
+        context.checking(new Expectations() {{
+            allowing(resolverMock).resolve("f");
+            will(returnValue(new File("f")));
+            allowing(taskResolverStub).resolveTask("c");
+            will(returnValue(task));
+        }});
+
+        assertThat(collection.getBuildDependencies().getDependencies(null), equalTo((Set) toSet(task)));
+    }
+
+    @Test
+    public void taskDependenciesContainsUnionOfDependenciesOfNestedFileCollectionsPlusOwnDependencies() {
+        final FileCollection fileCollectionMock = context.mock(FileCollection.class);
+
+        collection.from(fileCollectionMock);
+        collection.from("f");
+        collection.builtBy('b');
+
+        final Task taskA = context.mock(Task.class, "a");
+        final Task taskB = context.mock(Task.class, "b");
+        context.checking(new Expectations() {{
+            allowing(resolverMock).resolve("f");
+            will(returnValue(new File("f")));
+            TaskDependency dependency = context.mock(TaskDependency.class);
+            allowing(fileCollectionMock).getBuildDependencies();
+            will(returnValue(dependency));
+            allowing(dependency).getDependencies(null);
+            will(returnValue(toSet(taskA)));
+            allowing(taskResolverStub).resolveTask('b');
+            will(returnValue(taskB));
+        }});
+
+        assertThat(collection.getBuildDependencies().getDependencies(null), equalTo((Set) toSet(taskA, taskB)));
+    }
+
+    @Test
+    public void hasSpecifiedDependenciesWhenEmpty() {
+        collection.builtBy("task");
+
+        final Task task = context.mock(Task.class);
+        context.checking(new Expectations(){{
+            allowing(taskResolverStub).resolveTask("task");
+            will(returnValue(task));
+        }});
+
+        assertThat(collection.getBuildDependencies().getDependencies(null), equalTo((Set) toSet(task)));
+        assertThat(collection.getAsFileTree().getBuildDependencies().getDependencies(null), equalTo((Set) toSet(task)));
+        assertThat(collection.getAsFileTree().matching(HelperUtil.TEST_CLOSURE).getBuildDependencies().getDependencies(null), equalTo((Set) toSet(task)));
+    }
+    
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/DefaultConfigurableFileTreeTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/DefaultConfigurableFileTreeTest.groovy
new file mode 100644
index 0000000..4ffd00b
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/DefaultConfigurableFileTreeTest.groovy
@@ -0,0 +1,324 @@
+/*
+ * 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.internal.file.collections
+
+import org.gradle.api.InvalidUserDataException
+import org.gradle.api.Task
+import org.gradle.api.file.FileTree
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.api.internal.tasks.TaskResolver
+import org.gradle.api.tasks.StopExecutionException
+import org.gradle.api.tasks.TaskDependency
+import org.gradle.api.tasks.util.AbstractTestForPatternSet
+import org.gradle.api.tasks.util.PatternFilterable
+import org.gradle.api.tasks.util.PatternSet
+import org.gradle.util.JUnit4GroovyMockery
+import org.gradle.util.TemporaryFolder
+import org.gradle.util.WrapUtil
+import org.jmock.integration.junit4.JUnit4Mockery
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import static org.gradle.api.file.FileVisitorUtil.assertCanStopVisiting
+import static org.gradle.api.file.FileVisitorUtil.assertVisits
+import static org.gradle.api.tasks.AntBuilderAwareUtil.assertSetContainsForAllTypes
+import static org.gradle.util.Matchers.isEmpty
+import static org.hamcrest.Matchers.*
+import static org.junit.Assert.*
+
+/**
+ * @author Hans Dockter
+ */
+class DefaultConfigurableFileTreeTest extends AbstractTestForPatternSet {
+    JUnit4Mockery context = new JUnit4GroovyMockery();
+    TaskResolver taskResolverStub = context.mock(TaskResolver.class);
+    DefaultConfigurableFileTree fileSet
+    FileResolver fileResolverStub = [resolve: {it as File}] as FileResolver
+    @Rule public TemporaryFolder tmpDir = new TemporaryFolder();
+    File testDir = tmpDir.dir
+
+    PatternFilterable getPatternSet() {
+        return fileSet
+    }
+
+    Class getPatternSetType() {
+        DefaultConfigurableFileTree
+    }
+
+    @Before public void setUp() {
+        super.setUp()
+        fileSet = patternSetType.newInstance(testDir, fileResolverStub, taskResolverStub)
+    }
+
+    @Test public void testFileSetConstructionWithBaseDir() {
+        fileSet = new DefaultConfigurableFileTree(testDir, fileResolverStub, taskResolverStub)
+        assertEquals(testDir, fileSet.dir)
+    }
+
+    @Test public void testFileSetConstructionFromMap() {
+        fileSet = new DefaultConfigurableFileTree(fileResolverStub, taskResolverStub, dir: testDir, includes: ['include'])
+        assertEquals(testDir, fileSet.dir)
+        assertEquals(['include'] as Set, fileSet.includes)
+    }
+
+    @Test(expected = InvalidUserDataException) public void testFileSetConstructionWithNoBaseDirSpecified() {
+        DefaultConfigurableFileTree fileSet = new DefaultConfigurableFileTree([:], fileResolverStub, taskResolverStub)
+        fileSet.contains(new File('unknown'))
+    }
+
+    @Test public void testFileSetConstructionWithBaseDirAsString() {
+        DefaultConfigurableFileTree fileSet = new DefaultConfigurableFileTree(fileResolverStub, taskResolverStub, dir: 'dirname')
+        assertEquals(new File('dirname'), fileSet.dir);
+    }
+
+    @Test public void testResolveAddsADirectoryFileTree() {
+        FileCollectionResolveContext resolveContext = context.mock(FileCollectionResolveContext)
+
+        context.checking {
+            one(resolveContext).add(withParam(notNullValue()))
+            will { fileTree ->
+                assertThat(fileTree, instanceOf(DirectoryFileTree))
+                assertThat(fileTree.dir, equalTo(testDir))
+            }
+        }
+
+        fileSet.resolve(resolveContext)
+    }
+
+    @Test public void testResolveAddsBuildDependenciesIfNotEmpty() {
+        FileCollectionResolveContext resolveContext = context.mock(FileCollectionResolveContext)
+        fileSet.builtBy("classes")
+
+        context.checking {
+            one(resolveContext).add(withParam(instanceOf(DirectoryFileTree)))
+            one(resolveContext).add(withParam(instanceOf(TaskDependency)))
+        }
+
+        fileSet.resolve(resolveContext)
+    }
+    
+    @Test public void testCanScanForFiles() {
+        File included1 = new File(testDir, 'subDir/included1')
+        File included2 = new File(testDir, 'subDir2/included2')
+        [included1, included2].each {File file ->
+            file.parentFile.mkdirs()
+            file.text = 'some text'
+        }
+
+        assertThat(fileSet.files, equalTo([included1, included2] as Set))
+    }
+
+    @Test public void testCanVisitFiles() {
+        File included1 = new File(testDir, 'subDir/included1')
+        File included2 = new File(testDir, 'subDir2/included2')
+        [included1, included2].each {File file ->
+            file.parentFile.mkdirs()
+            file.text = 'some text'
+        }
+
+        assertVisits(fileSet, ['subDir/included1', 'subDir2/included2'], ['subDir', 'subDir2'])
+    }
+
+    @Test public void testCanStopVisitingFiles() {
+        File included1 = new File(testDir, 'subDir/included1')
+        File included2 = new File(testDir, 'subDir/otherDir/included2')
+        [included1, included2].each {File file ->
+            file.parentFile.mkdirs()
+            file.text = 'some text'
+        }
+
+        assertCanStopVisiting(fileSet)
+    }
+
+    @Test public void testContainsFiles() {
+        File included1 = new File(testDir, 'subDir/included1')
+        File included2 = new File(testDir, 'subDir2/included2')
+        [included1, included2].each {File file ->
+            file.parentFile.mkdirs()
+            file.text = 'some text'
+        }
+
+        assertTrue(fileSet.contains(included1))
+        assertTrue(fileSet.contains(included2))
+        assertFalse(fileSet.contains(testDir))
+        assertFalse(fileSet.contains(included1.parentFile))
+        assertFalse(fileSet.contains(included2.parentFile))
+        assertFalse(fileSet.contains(new File(testDir, 'does not exist')))
+        assertFalse(fileSet.contains(testDir.parentFile))
+        assertFalse(fileSet.contains(new File('something')))
+    }
+
+    @Test public void testCanAddToAntTask() {
+        File included1 = new File(testDir, 'subDir/included1')
+        File included2 = new File(testDir, 'subDir2/included2')
+        [included1, included2].each {File file ->
+            file.parentFile.mkdirs()
+            file.text = 'some text'
+        }
+
+        assertSetContainsForAllTypes(fileSet, 'subDir/included1', 'subDir2/included2')
+    }
+
+    @Test public void testIsEmptyWhenBaseDirDoesNotExist() {
+        fileSet.dir = new File(testDir, 'does not exist')
+
+        assertThat(fileSet.files, isEmpty())
+        assertSetContainsForAllTypes(fileSet)
+        assertVisits(fileSet, [], [])
+    }
+
+    @Test public void testCanSelectFilesUsingPatterns() {
+        File included1 = new File(testDir, 'subDir/included1')
+        File included2 = new File(testDir, 'subDir2/included2')
+        File excluded1 = new File(testDir, 'subDir/notincluded')
+        File ignored1 = new File(testDir, 'ignored')
+        [included1, included2, excluded1, ignored1].each {File file ->
+            file.parentFile.mkdirs()
+            file.text = 'some text'
+        }
+
+        fileSet.include('*/*included*')
+        fileSet.exclude('**/not*')
+
+        assertThat(fileSet.files, equalTo([included1, included2] as Set))
+        assertSetContainsForAllTypes(fileSet, 'subDir/included1', 'subDir2/included2')
+        assertVisits(fileSet, ['subDir/included1', 'subDir2/included2'], ['subDir', 'subDir2'])
+        assertTrue(fileSet.contains(included1))
+        assertFalse(fileSet.contains(excluded1))
+        assertFalse(fileSet.contains(ignored1))
+    }
+
+    @Test public void testCanFilterMatchingFilesUsingConfigureClosure() {
+        File included1 = new File(testDir, 'subDir/included1')
+        File included2 = new File(testDir, 'subDir2/included2')
+        File excluded1 = new File(testDir, 'subDir/notincluded')
+        File ignored1 = new File(testDir, 'ignored')
+        [included1, included2, excluded1, ignored1].each {File file ->
+            file.parentFile.mkdirs()
+            file.text = 'some text'
+        }
+
+        FileTree filtered = fileSet.matching {
+            include('*/*included*')
+            exclude('**/not*')
+        }
+
+        assertThat(filtered.files, equalTo([included1, included2] as Set))
+        assertSetContainsForAllTypes(filtered, 'subDir/included1', 'subDir2/included2')
+        assertVisits(filtered, ['subDir/included1', 'subDir2/included2'], ['subDir', 'subDir2'])
+        assertTrue(filtered.contains(included1))
+        assertFalse(filtered.contains(excluded1))
+        assertFalse(filtered.contains(ignored1))
+    }
+
+    @Test public void testCanFilterMatchingFilesUsingPatternSet() {
+        File included1 = new File(testDir, 'subDir/included1')
+        File included2 = new File(testDir, 'subDir2/included2')
+        File excluded1 = new File(testDir, 'subDir/notincluded')
+        File ignored1 = new File(testDir, 'ignored')
+        [included1, included2, excluded1, ignored1].each {File file ->
+            file.parentFile.mkdirs()
+            file.text = 'some text'
+        }
+
+        PatternSet patternSet = new PatternSet(includes: ['*/*included*'], excludes: ['**/not*'])
+        FileTree filtered = fileSet.matching(patternSet)
+
+        assertThat(filtered.files, equalTo([included1, included2] as Set))
+        assertSetContainsForAllTypes(filtered, 'subDir/included1', 'subDir2/included2')
+        assertVisits(filtered, ['subDir/included1', 'subDir2/included2'], ['subDir', 'subDir2'])
+        assertTrue(filtered.contains(included1))
+        assertFalse(filtered.contains(excluded1))
+        assertFalse(filtered.contains(ignored1))
+    }
+
+    @Test public void testCanFilterAndSelectFiles() {
+        File included1 = new File(testDir, 'subDir/included1')
+        File included2 = new File(testDir, 'subDir2/included2')
+        File excluded1 = new File(testDir, 'subDir/notincluded')
+        File excluded2 = new File(testDir, 'subDir/excluded')
+        File ignored1 = new File(testDir, 'ignored')
+        [included1, included2, excluded1, excluded2, ignored1].each {File file ->
+            file.parentFile.mkdirs()
+            file.text = 'some text'
+        }
+
+        fileSet.exclude '**/excluded*'
+
+        FileTree filtered = fileSet.matching {
+            include('*/*included*')
+            exclude('**/not*')
+        }
+
+        assertThat(filtered.files, equalTo([included1, included2] as Set))
+        assertSetContainsForAllTypes(filtered, 'subDir/included1', 'subDir2/included2')
+        assertVisits(filtered, ['subDir/included1', 'subDir2/included2'], ['subDir', 'subDir2'])
+        assertTrue(filtered.contains(included1))
+        assertFalse(filtered.contains(excluded1))
+        assertFalse(filtered.contains(ignored1))
+    }
+
+    @Test public void testDisplayName() {
+        assertThat(fileSet.displayName, equalTo("directory '$testDir'".toString()))
+    }
+
+    @Test public void testStopExecutionIfEmptyWhenNoMatchingFilesFound() {
+        fileSet.include('**/*included')
+        new File(testDir, 'excluded').text = 'some text'
+
+        try {
+            fileSet.stopExecutionIfEmpty()
+            fail()
+        } catch (StopExecutionException e) {
+            assertThat(e.message, equalTo("Directory '$testDir' does not contain any files." as String))
+        }
+    }
+
+    @Test public void testStopExecutionIfEmptyWhenMatchingFilesFound() {
+        fileSet.include('**/*included')
+        new File(testDir, 'included').text = 'some text'
+
+        fileSet.stopExecutionIfEmpty()
+    }
+
+    @Test
+    public void canGetAndSetTaskDependencies() {
+        FileResolver fileResolverStub = context.mock(FileResolver.class);
+        fileSet = patternSetType.newInstance(testDir, fileResolverStub, taskResolverStub)
+
+        assertThat(fileSet.getBuiltBy(), isEmpty());
+
+        fileSet.builtBy("a");
+        fileSet.builtBy("b");
+        fileSet.from("f");
+
+        assertThat(fileSet.getBuiltBy(), equalTo(WrapUtil.toSet((Object) "a", "b")));
+
+        fileSet.setBuiltBy(WrapUtil.toList("c"));
+
+        assertThat(fileSet.getBuiltBy(), equalTo(WrapUtil.toSet((Object) "c")));
+        final Task task = context.mock(Task.class);
+        context.checking {
+            allowing(fileResolverStub).resolve("f");
+            will(returnValue(new File("f")));
+            allowing(taskResolverStub).resolveTask('c');
+            will(returnValue(task));
+        }
+
+        assertThat(fileSet.getBuildDependencies().getDependencies(null), equalTo((Set) WrapUtil.toSet(task)));
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/DefaultFileCollectionResolveContextTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/DefaultFileCollectionResolveContextTest.groovy
new file mode 100644
index 0000000..9d999aa
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/DefaultFileCollectionResolveContextTest.groovy
@@ -0,0 +1,401 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections
+
+import java.util.concurrent.Callable
+
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.api.tasks.TaskDependency
+import spock.lang.Specification
+import org.gradle.api.file.FileTree
+import org.gradle.api.file.FileCollection
+
+class DefaultFileCollectionResolveContextTest extends Specification {
+    final FileResolver resolver = Mock()
+    final DefaultFileCollectionResolveContext context = new DefaultFileCollectionResolveContext(resolver)
+
+    def resolveAsFileCollectionReturnsEmptyListWhenContextIsEmpty() {
+        expect:
+        context.resolveAsFileCollections() == []
+    }
+
+    def resolveAsFileTreeReturnsEmptyListWhenContextIsEmpty() {
+        expect:
+        context.resolveAsFileTrees() == []
+    }
+
+    def resolveAsMinimalFileCollectionReturnsEmptyListWhenContextIsEmpty() {
+        expect:
+        context.resolveAsMinimalFileCollections() == []
+    }
+
+    def resolveAsFileCollectionWrapsAMinimalFileSet() {
+        MinimalFileSet fileSet = Mock()
+
+        when:
+        context.add(fileSet)
+        def result = context.resolveAsFileCollections()
+
+        then:
+        result.size() == 1
+        result[0] instanceof FileCollectionAdapter
+        result[0].fileCollection == fileSet
+    }
+
+    def resolveAsFileTreeConvertsTheElementsOfMinimalFileSet() {
+        MinimalFileSet fileSet = Mock()
+        File file = this.file('file1')
+        File dir = directory('file2')
+        File doesNotExist = nonExistent('file3')
+
+        when:
+        context.add(fileSet)
+        def result = context.resolveAsFileTrees()
+
+        then:
+        result.size() == 2
+        result[0] instanceof FileTreeAdapter
+        result[0].tree instanceof SingletonFileTree
+        result[0].tree.file == file
+        result[1] instanceof FileTreeAdapter
+        result[1].tree instanceof DirectoryFileTree
+        result[1].tree.dir == dir
+        1 * fileSet.files >> ([file, dir, doesNotExist] as LinkedHashSet)
+    }
+
+    def resolveAsMinimalFileCollectionReturnsMinimalFileSet() {
+        MinimalFileSet fileSet = Mock()
+
+        when:
+        context.add(fileSet)
+        def result = context.resolveAsMinimalFileCollections()
+
+        then:
+        result == [fileSet]
+    }
+
+    def resolveAsFileCollectionWrapsAMinimalFileTree() {
+        MinimalFileTree fileTree = Mock()
+
+        when:
+        context.add(fileTree)
+        def result = context.resolveAsFileCollections()
+
+        then:
+        result.size() == 1
+        result[0] instanceof FileTreeAdapter
+        result[0].tree == fileTree
+    }
+
+    def resolveAsFileTreesWrapsAMinimalFileTree() {
+        MinimalFileTree fileTree = Mock()
+
+        when:
+        context.add(fileTree)
+        def result = context.resolveAsFileTrees()
+
+        then:
+        result.size() == 1
+        result[0] instanceof FileTreeAdapter
+        result[0].tree == fileTree
+    }
+
+    def resolveAsMinimalFileCollectionWrapsAMinimalFileTree() {
+        MinimalFileTree fileTree = Mock()
+
+        when:
+        context.add(fileTree)
+        def result = context.resolveAsMinimalFileCollections()
+
+        then:
+        result == [fileTree]
+    }
+
+    def resolveAsFileCollectionsForAFileCollection() {
+        FileCollection fileCollection = Mock()
+
+        when:
+        context.add(fileCollection)
+        def result = context.resolveAsFileCollections()
+
+        then:
+        result == [fileCollection]
+    }
+
+    def resolveAsFileCollectionsDelegatesToACompositeFileCollection() {
+        FileCollectionContainer composite = Mock()
+        FileCollection contents = Mock()
+
+        when:
+        context.add(composite)
+        def result = context.resolveAsFileCollections()
+
+        then:
+        result == [contents]
+        1 * composite.resolve(!null) >> { it[0].add(contents) }
+    }
+
+    def resolveAsFileTreesDelegatesToACompositeFileCollection() {
+        FileCollectionContainer composite = Mock()
+        FileTree contents = Mock()
+
+        when:
+        context.add(composite)
+        def result = context.resolveAsFileTrees()
+
+        then:
+        result == [contents]
+        1 * composite.resolve(!null) >> { it[0].add(contents) }
+    }
+
+    def resolveAsMinimalFileCollectionsDelegatesToACompositeFileCollection() {
+        FileCollectionContainer composite = Mock()
+        MinimalFileCollection contents = Mock()
+
+        when:
+        context.add(composite)
+        def result = context.resolveAsMinimalFileCollections()
+
+        then:
+        result == [contents]
+        1 * composite.resolve(!null) >> { it[0].add(contents) }
+    }
+
+    def resolvesCompositeFileCollectionsInDepthwiseOrder() {
+        FileCollectionContainer parent1 = Mock()
+        FileCollection child1 = Mock()
+        FileCollectionContainer parent2 = Mock()
+        FileCollection child2 = Mock()
+        FileCollection child3 = Mock()
+
+        when:
+        context.add(parent1)
+        context.add(child3)
+        def result = context.resolveAsFileCollections()
+
+        then:
+        result == [child1, child2, child3]
+        1 * parent1.resolve(!null) >> { it[0].add(child1); it[0].add(parent2) }
+        1 * parent2.resolve(!null) >> { it[0].add(child2) }
+    }
+
+    def recursivelyResolvesReturnValueOfAClosure() {
+        FileCollection content = Mock()
+
+        when:
+        context.add { content }
+        def result = context.resolveAsFileCollections()
+
+        then:
+        result == [content]
+    }
+
+    def resolvesAClosureWhichReturnsNull() {
+        when:
+        context.add { null }
+        def result = context.resolveAsFileCollections()
+
+        then:
+        result == []
+    }
+
+    def recursivelyResolvesReturnValueOfACallable() {
+        FileCollection content = Mock()
+        Callable<?> callable = Mock()
+
+        when:
+        context.add(callable)
+        def result = context.resolveAsFileCollections()
+
+        then:
+        1 * callable.call() >> content
+        result == [content]
+    }
+
+    def resolvesACallableWhichReturnsNull() {
+        Callable<?> callable = Mock()
+
+        when:
+        context.add(callable)
+        def result = context.resolveAsFileCollections()
+
+        then:
+        1 * callable.call() >> null
+        result == []
+    }
+
+    def recursivelyResolvesElementsOfAnIterable() {
+        FileCollection content = Mock()
+        Iterable<Object> iterable = Mock()
+
+        when:
+        context.add(iterable)
+        def result = context.resolveAsFileCollections()
+
+        then:
+        1 * iterable.iterator() >> [content].iterator()
+        result == [content]
+    }
+
+    def recursivelyResolvesElementsAnArray() {
+        FileCollection content = Mock()
+
+        when:
+        context.add([content] as Object[])
+        def result = context.resolveAsFileCollections()
+
+        then:
+        result == [content]
+    }
+
+    def resolveAsFileCollectionsIgnoresATaskDependency() {
+        TaskDependency dependency = Mock()
+
+        when:
+        context.add(dependency)
+        def result = context.resolveAsFileCollections()
+
+        then:
+        result == []
+    }
+
+    def resolveAsFileTreesIgnoresATaskDependency() {
+        TaskDependency dependency = Mock()
+
+        when:
+        context.add(dependency)
+        def result = context.resolveAsFileTrees()
+
+        then:
+        result == []
+    }
+
+    def resolveAsMinimalFileCollectionsIgnoresATaskDependency() {
+        TaskDependency dependency = Mock()
+
+        when:
+        context.add(dependency)
+        def result = context.resolveAsMinimalFileCollections()
+
+        then:
+        result == []
+    }
+
+    def resolveAsFileCollectionsUsesFileResolverToResolveOtherTypes() {
+        File file1 = new File('a')
+        File file2 = new File('b')
+
+        when:
+        context.add('a')
+        context.add('b')
+        def result = context.resolveAsFileCollections()
+
+        then:
+        result.size() == 2
+        result[0] instanceof FileCollectionAdapter
+        result[0].fileCollection instanceof ListBackedFileSet
+        result[0].fileCollection.files as List == [file1]
+        result[1] instanceof FileCollectionAdapter
+        result[1].fileCollection instanceof ListBackedFileSet
+        result[1].fileCollection.files as List == [file2]
+        1 * resolver.resolve('a') >> file1
+        1 * resolver.resolve('b') >> file2
+    }
+
+    def resolveAsFileTreeUsesFileResolverToResolveOtherTypes() {
+        File file = file('a')
+
+        when:
+        context.add('a')
+        def result = context.resolveAsFileTrees()
+
+        then:
+        result.size() == 1
+        result[0] instanceof FileTreeAdapter
+        result[0].tree instanceof SingletonFileTree
+        result[0].tree.file == file
+        1 * resolver.resolve('a') >> file
+    }
+
+    def resolveAsMinimalFileCollectionUsesFileResolverToResolveOtherTypes() {
+        File file = file('a')
+
+        when:
+        context.add('a')
+        def result = context.resolveAsMinimalFileCollections()
+
+        then:
+        result.size() == 1
+        result[0] instanceof ListBackedFileSet
+        result[0].files as List == [file]
+        1 * resolver.resolve('a') >> file
+    }
+
+    def canPushContextWhichUsesADifferentFileResolverToConvertToFileCollections() {
+        FileResolver fileResolver = Mock()
+        File file = new File('a')
+
+        when:
+        context.push(fileResolver).add('a')
+        def result = context.resolveAsFileCollections()
+
+        then:
+        result.size() == 1
+        result[0] instanceof FileCollectionAdapter
+        result[0].fileCollection instanceof ListBackedFileSet
+        result[0].fileCollection.files as List == [file]
+        1 * fileResolver.resolve('a') >> file
+        0 * _._
+    }
+
+    def canPushContextWhichUsesADifferentFileResolverToConvertToFileTrees() {
+        FileResolver fileResolver = Mock()
+        File file = file('a')
+
+        when:
+        context.push(fileResolver).add('a')
+        def result = context.resolveAsFileTrees()
+
+        then:
+        result.size() == 1
+        result[0] instanceof FileTreeAdapter
+        result[0].tree instanceof SingletonFileTree
+        result[0].tree.file == file
+        1 * fileResolver.resolve('a') >> file
+    }
+
+    def file(String name) {
+        File f = Mock()
+        _ * f.file >> true
+        _ * f.exists() >> true
+        _ * f.canonicalFile >> f
+        f
+    }
+
+    def directory(String name) {
+        File f = Mock()
+        _ * f.directory >> true
+        _ * f.exists() >> true
+        _ * f.canonicalFile >> f
+        f
+    }
+
+    def nonExistent(String name) {
+        File f = Mock()
+        _ * f.canonicalFile >> f
+        f
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/DirectoryFileTreeTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/DirectoryFileTreeTest.java
new file mode 100644
index 0000000..892d893
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/DirectoryFileTreeTest.java
@@ -0,0 +1,375 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections;
+
+import org.gradle.api.file.FileVisitDetails;
+import org.gradle.api.file.RelativePath;
+import org.gradle.api.internal.file.copy.CopySpecVisitor;
+import org.gradle.api.specs.Spec;
+import org.gradle.api.tasks.util.PatternSet;
+import org.gradle.util.JUnit4GroovyMockery;
+import org.gradle.util.TemporaryFolder;
+import org.gradle.util.TestFile;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.jmock.Sequence;
+import org.jmock.api.Action;
+import org.jmock.api.Invocation;
+import org.jmock.integration.junit4.JMock;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.*;
+
+ at RunWith(JMock.class)
+public class DirectoryFileTreeTest {
+    @Rule
+    public final TemporaryFolder tmpDir = new TemporaryFolder();
+    private JUnit4Mockery context = new JUnit4GroovyMockery();
+    private CopySpecVisitor visitor;
+
+    @Before
+    public void setUp() {
+        visitor = context.mock(CopySpecVisitor.class);
+    }
+
+    @Test
+    public void rootDirEmpty() throws IOException {
+        final MockFile root = new MockFile(context, "root", false);
+        root.setExpectations();
+
+        DirectoryFileTree fileTree = new DirectoryFileTree(root.getMock());
+        root.setExpectations();
+
+        fileTree.visit(visitor);
+    }
+
+    @Test
+    public void testUsesSpecFromPatternSetToMatchFilesAndDirs() {
+        final PatternSet patternSet = context.mock(PatternSet.class);
+        final Spec spec = context.mock(Spec.class);
+
+        context.checking(new Expectations() {{
+            one(patternSet).getAsSpec();
+            will(returnValue(spec));
+        }});
+
+        DirectoryFileTree fileTree = new DirectoryFileTree(new File("root"), patternSet);
+        fileTree.visit(visitor);
+    }
+
+    @Test
+    public void walkSingleFile() throws IOException {
+
+        final MockFile root = new MockFile(context, "root", false);
+        final MockFile fileToCopy = root.addFile("file.txt");
+
+        fileToCopy.setExpectations();
+
+        context.checking(new Expectations() {{
+            one(visitor).visitFile(with(file(fileToCopy)));
+        }});
+
+        DirectoryFileTree fileTree = new DirectoryFileTree(fileToCopy.getMock());
+        fileTree.visit(visitor);
+    }
+
+    /*
+    mock file structure:
+    root
+        rootFile1
+        dir1
+           dirFile1
+           dirFile2
+        rootFile2
+
+        Test that the files are really walked breadth first
+     */
+    @Test
+    public void walkBreadthFirst() throws IOException {
+        final MockFile root = new MockFile(context, "root", false);
+        final MockFile rootFile1 = root.addFile("rootFile1");
+        final MockFile dir1 = root.addDir("dir1");
+        final MockFile dirFile1 = dir1.addFile("dirFile1");
+        final MockFile dirFile2 = dir1.addFile("dirFile2");
+        final MockFile rootFile2 = root.addFile("rootFile2");
+        root.setExpectations();
+
+        final Sequence visiting = context.sequence("visiting");
+        context.checking(new Expectations() {{
+            one(visitor).visitFile(with(file(rootFile1)));
+            inSequence(visiting);
+            one(visitor).visitFile(with(file(rootFile2)));
+            inSequence(visiting);
+            one(visitor).visitDir(with(file(dir1)));
+            inSequence(visiting);
+            one(visitor).visitFile(with(file(dirFile1)));
+            inSequence(visiting);
+            one(visitor).visitFile(with(file(dirFile2)));
+            inSequence(visiting);
+        }});
+
+        DirectoryFileTree fileTree = new DirectoryFileTree(root.getMock());
+        fileTree.visit(visitor);
+    }
+
+    @Test
+    public void walkDepthFirst() throws IOException {
+
+        final MockFile root = new MockFile(context, "root", false);
+        final MockFile rootFile1 = root.addFile("rootFile1");
+        final MockFile dir1 = root.addDir("dir1");
+        final MockFile dirFile1 = dir1.addFile("dirFile1");
+        final MockFile dirFile2 = dir1.addFile("dirFile2");
+        final MockFile rootFile2 = root.addFile("rootFile2");
+        root.setExpectations();
+
+        final Sequence visiting = context.sequence("visiting");
+        context.checking(new Expectations() {{
+            one(visitor).visitFile(with(file(rootFile1)));
+            inSequence(visiting);
+            one(visitor).visitFile(with(file(rootFile2)));
+            inSequence(visiting);
+            one(visitor).visitFile(with(file(dirFile1)));
+            inSequence(visiting);
+            one(visitor).visitFile(with(file(dirFile2)));
+            inSequence(visiting);
+            one(visitor).visitDir(with(file(dir1)));
+            inSequence(visiting);
+        }});
+
+        DirectoryFileTree fileTree = new DirectoryFileTree(root.getMock()).depthFirst();
+        fileTree.visit(visitor);
+    }
+
+    @Test
+    public void canApplyFilter() throws IOException {
+        final MockFile root = new MockFile(context, "root", false);
+        root.addFile("rootFile1");
+        final MockFile dir1 = root.addDir("dir1");
+        dir1.addFile("dirFile1");
+        final MockFile dirFile2 = dir1.addFile("dirFile2");
+        root.addFile("rootFile2");
+        root.setExpectations();
+
+        final Sequence visiting = context.sequence("visiting");
+        context.checking(new Expectations() {{
+            one(visitor).visitDir(with(file(dir1)));
+            inSequence(visiting);
+            one(visitor).visitFile(with(file(dirFile2)));
+            inSequence(visiting);
+        }});
+
+        PatternSet patterns = new PatternSet();
+        patterns.include("**/*2");
+        PatternSet filter = new PatternSet();
+        filter.include("dir1/**");
+        DirectoryFileTree fileTree = new DirectoryFileTree(root.getMock(), patterns).filter(filter);
+        fileTree.visit(visitor);
+    }
+
+    @Test
+    public void canVisitorCanStopVisit() throws IOException {
+        final MockFile root = new MockFile(context, "root", false);
+        final MockFile rootFile1 = root.addFile("rootFile1");
+        final MockFile dir1 = root.addDir("dir1");
+        final MockFile dirFile1 = dir1.addFile("dirFile1");
+        dir1.addFile("dirFile2");
+        dir1.addDir("dir1Dir").addFile("dir1Dir1File1");
+        final MockFile rootFile2 = root.addFile("rootFile2");
+        root.setExpectations();
+
+        context.checking(new Expectations() {{
+            one(visitor).visitFile(with(file(rootFile1)));
+            will(stopVisiting());
+        }});
+
+        DirectoryFileTree fileTree = new DirectoryFileTree(root.getMock());
+        fileTree.visit(visitor);
+
+        final Sequence visiting = context.sequence("visiting");
+        context.checking(new Expectations() {{
+            one(visitor).visitFile(with(file(rootFile1)));
+            inSequence(visiting);
+            one(visitor).visitFile(with(file(rootFile2)));
+            inSequence(visiting);
+            one(visitor).visitDir(with(file(dir1)));
+            inSequence(visiting);
+            one(visitor).visitFile(with(file(dirFile1)));
+            will(stopVisiting());
+            inSequence(visiting);
+        }});
+
+        fileTree.visit(visitor);
+    }
+
+    @Test
+    public void canTestForFileMembership() {
+        TestFile rootDir = tmpDir.createDir("root");
+        TestFile rootTextFile = rootDir.file("a.txt").createFile();
+        TestFile nestedTextFile = rootDir.file("a/b/c.txt").createFile();
+        TestFile notTextFile = rootDir.file("a/b/c.html").createFile();
+        TestFile excludedFile = rootDir.file("subdir1/a/b/c.html").createFile();
+        TestFile notUnderRoot = tmpDir.createDir("root2").file("a.txt").createFile();
+        TestFile doesNotExist = rootDir.file("b.txt");
+
+        PatternSet patterns = new PatternSet();
+        patterns.include("**/*.txt");
+        patterns.exclude("subdir1/**");
+        DirectoryFileTree fileTree = new DirectoryFileTree(rootDir, patterns);
+
+        assertTrue(fileTree.contains(rootTextFile));
+        assertTrue(fileTree.contains(nestedTextFile));
+        assertFalse(fileTree.contains(notTextFile));
+        assertFalse(fileTree.contains(excludedFile));
+        assertFalse(fileTree.contains(notUnderRoot));
+        assertFalse(fileTree.contains(doesNotExist));
+    }
+
+    @Test
+    public void hasUsefulDisplayName() {
+        DirectoryFileTree treeWithNoIncludesOrExcludes = new DirectoryFileTree(tmpDir.getDir());
+        PatternSet includesOnly = new PatternSet();
+        includesOnly.include("a/b", "c");
+        DirectoryFileTree treeWithIncludes = new DirectoryFileTree(tmpDir.getDir(), includesOnly);
+        PatternSet excludesOnly = new PatternSet();
+        excludesOnly.exclude("a/b", "c");
+        DirectoryFileTree treeWithExcludes = new DirectoryFileTree(tmpDir.getDir(), excludesOnly);
+
+        assertThat(treeWithNoIncludesOrExcludes.getDisplayName(), equalTo(String.format("directory '%s'", tmpDir.getDir())));
+        assertThat(treeWithIncludes.getDisplayName(), equalTo(String.format("directory '%s' include 'a/b', 'c'", tmpDir.getDir())));
+        assertThat(treeWithExcludes.getDisplayName(), equalTo(String.format("directory '%s' exclude 'a/b', 'c'", tmpDir.getDir())));
+    }
+
+    private Action stopVisiting() {
+        return new Action() {
+            public void describeTo(Description description) {
+                description.appendText("stop visiting");
+            }
+
+            public Object invoke(Invocation invocation) throws Throwable {
+                FileVisitDetails details = (FileVisitDetails) invocation.getParameter(0);
+                details.stopVisiting();
+                return null;
+            }
+        };
+    }
+
+    // test excludes, includes
+
+    private Matcher<FileVisitDetails> file(final MockFile file) {
+        return new BaseMatcher<FileVisitDetails>() {
+            public boolean matches(Object o) {
+                FileVisitDetails details = (FileVisitDetails) o;
+                return details.getFile().equals(file.getMock()) && details.getRelativePath().equals(file.getRelativePath());
+            }
+
+            public void describeTo(Description description) {
+                description.appendText("details match file ").appendValue(file.getMock()).appendText(" with path ")
+                        .appendValue(file.getRelativePath());
+            }
+        };
+    }
+
+    public class MockFile {
+        private boolean isFile;
+        private String name;
+        private Mockery context;
+        private List<MockFile> children;
+        private File mock;
+        private MockFile parent;
+
+        public MockFile(Mockery context, String name, boolean isFile) {
+            this.context = context;
+            this.name = name;
+            this.isFile = isFile;
+            children = new ArrayList<MockFile>();
+            mock = context.mock(File.class, name);
+        }
+
+        public File getMock() {
+            return mock;
+        }
+
+        public MockFile addFile(String name) {
+            MockFile child = new MockFile(context, name, true);
+            child.setParent(this);
+            children.add(child);
+            return child;
+        }
+
+        public MockFile addDir(String name) {
+            MockFile child = new MockFile(context, name, false);
+            child.setParent(this);
+            children.add(child);
+            return child;
+        }
+
+        public void setParent(MockFile parent) {
+            this.parent = parent;
+        }
+
+        public RelativePath getRelativePath() {
+            if (parent == null) {
+                return new RelativePath(isFile);
+            } else {
+                return parent.getRelativePath().append(isFile, name);
+            }
+        }
+
+        public void setExpectations() {
+            Expectations expectations = new Expectations();
+            setExpectations(expectations);
+            context.checking(expectations);
+        }
+
+        public void setExpectations(Expectations expectations) {
+            try {
+                expectations.allowing(mock).getCanonicalFile();
+                expectations.will(expectations.returnValue(mock));
+            } catch (IOException th) {
+                // ignore
+            }
+            expectations.allowing(mock).isFile();
+            expectations.will(expectations.returnValue(isFile));
+            expectations.allowing(mock).getName();
+            expectations.will(expectations.returnValue(name));
+            expectations.allowing(mock).exists();
+            expectations.will(expectations.returnValue(true));
+
+            ArrayList<File> mockChildren = new ArrayList<File>(children.size());
+            for (MockFile child : children) {
+                mockChildren.add(child.getMock());
+                child.setExpectations(expectations);
+            }
+            expectations.allowing(mock).listFiles();
+            expectations.will(expectations.returnValue(mockChildren.toArray(new File[mockChildren.size()])));
+        }
+    }
+
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/FileCollectionAdapterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/FileCollectionAdapterTest.groovy
new file mode 100644
index 0000000..861ba38
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/FileCollectionAdapterTest.groovy
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections
+
+import spock.lang.Specification
+import org.gradle.api.Buildable
+import org.gradle.api.tasks.TaskDependency
+
+class FileCollectionAdapterTest extends Specification {
+
+    def delegatesToTargetCollectionToBuildSetOfFiles() {
+        MinimalFileSet fileSet = Mock()
+        FileCollectionAdapter adapter = new FileCollectionAdapter(fileSet)
+        def expectedFiles = [new File('a'), new File('b')] as Set
+
+        when:
+        def files = adapter.files
+
+        then:
+        files == expectedFiles
+        1 * fileSet.getFiles() >> expectedFiles
+        0 * _._
+    }
+
+    def resolveAddsTargetCollectionToContext() {
+        MinimalFileSet fileSet = Mock()
+        FileCollectionAdapter adapter = new FileCollectionAdapter(fileSet)
+        FileCollectionResolveContext context = Mock()
+
+        when:
+        adapter.resolve(context)
+
+        then:
+        1 * context.add(fileSet)
+        0 * _._
+    }
+
+    def getBuildDependenciesDelegatesToTargetCollectionWhenItImplementsBuildable() {
+        TestFileSet fileSet = Mock()
+        TaskDependency expectedDependency = Mock()
+        FileCollectionAdapter adapter = new FileCollectionAdapter(fileSet)
+
+        when:
+        def dependencies = adapter.buildDependencies
+
+        then:
+        dependencies == expectedDependency
+        1 * fileSet.buildDependencies >> expectedDependency
+    }
+}
+
+interface TestFileSet extends MinimalFileSet, Buildable {
+
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/FileTreeAdapterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/FileTreeAdapterTest.groovy
new file mode 100644
index 0000000..6ece488
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/FileTreeAdapterTest.groovy
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections
+
+import org.gradle.api.Buildable
+import org.gradle.api.file.FileVisitDetails
+import org.gradle.api.file.FileVisitor
+import org.gradle.api.tasks.TaskDependency
+import org.gradle.api.tasks.util.PatternFilterable
+import spock.lang.Specification
+
+class FileTreeAdapterTest extends Specification {
+    def toStringUsesDisplayName() {
+        MinimalFileTree tree = Mock()
+        _ * tree.displayName >> 'display name'
+
+        FileTreeAdapter adapter = new FileTreeAdapter(tree)
+
+        expect:
+        adapter.toString() == 'display name'
+    }
+
+    def visitDelegatesToTargetTree() {
+        MinimalFileTree tree = Mock()
+        FileTreeAdapter adapter = new FileTreeAdapter(tree)
+        FileVisitor visitor = Mock()
+
+        when:
+        adapter.visit(visitor)
+
+        then:
+        1 * tree.visit(visitor)
+        0 * _._
+    }
+
+    def resolveAddsTargetTreeToContext() {
+        MinimalFileTree tree = Mock()
+        FileTreeAdapter adapter = new FileTreeAdapter(tree)
+        FileCollectionResolveContext context = Mock()
+
+        when:
+        adapter.resolve(context)
+
+        then:
+        1 * context.add(tree)
+        0 * _._
+    }
+
+    def getAsFileTreesConvertsMirroringFileTreeByVisitingAllElementsAndReturningLocalMirror() {
+        FileSystemMirroringFileTree tree = Mock()
+        FileTreeAdapter adapter = new FileTreeAdapter(tree)
+        DirectoryFileTree mirror = new DirectoryFileTree(new File('a'))
+
+        when:
+        def result = adapter.asFileTrees
+
+        then:
+        result == [mirror]
+        1 * tree.visit(!null) >> { it[0].visitFile({} as FileVisitDetails) }
+        1 * tree.mirror >> mirror
+        0 * _._
+    }
+
+    def getAsFileTreesConvertsEmptyMirroringTree() {
+        FileSystemMirroringFileTree tree = Mock()
+        FileTreeAdapter adapter = new FileTreeAdapter(tree)
+
+        when:
+        def result = adapter.asFileTrees
+
+        then:
+        result == []
+        1 * tree.visit(!null)
+        0 * _._
+    }
+
+    def getAsFileTreesConvertsLocalFileTree() {
+        LocalFileTree tree = Mock()
+        DirectoryFileTree contents = Mock()
+        FileTreeAdapter adapter = new FileTreeAdapter(tree)
+
+        when:
+        def result = adapter.asFileTrees
+
+        then:
+        result == [contents]
+        1 * tree.localContents >> [contents]
+        0 * _._
+    }
+
+    def getBuildDependenciesDelegatesToTargetTreeWhenItImplementsBuildable() {
+        TestFileTree tree = Mock()
+        TaskDependency expectedDependency = Mock()
+        FileTreeAdapter adapter = new FileTreeAdapter(tree)
+
+        when:
+        def dependencies = adapter.buildDependencies
+
+        then:
+        dependencies == expectedDependency
+        1 * tree.buildDependencies >> expectedDependency
+    }
+
+    def matchingDelegatesToTargetTreeWhenItImplementsPatternFilterableFileTree() {
+        PatternFilterableFileTree tree = Mock()
+        MinimalFileTree filtered = Mock()
+        PatternFilterable filter = Mock()
+        FileTreeAdapter adapter = new FileTreeAdapter(tree)
+
+        when:
+        def filteredAdapter = adapter.matching(filter)
+
+        then:
+        filteredAdapter instanceof FileTreeAdapter
+        filteredAdapter.tree == filtered
+        1 * tree.filter(filter) >> filtered
+    }
+    
+    def containsDelegatesToTargetTreeWhenItImplementsRandomAccessFileCollection() {
+        TestFileTree tree = Mock()
+        File f = new File('a')
+        FileTreeAdapter adapter = new FileTreeAdapter(tree)
+
+        when:
+        def result = adapter.contains(f)
+
+        then:
+        result
+        1 * tree.contains(f) >> true
+    }
+}
+
+interface TestFileTree extends MinimalFileTree, Buildable, RandomAccessFileCollection {
+}
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/ListBackedFileSetTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/ListBackedFileSetTest.groovy
new file mode 100644
index 0000000..ba0ba78
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/ListBackedFileSetTest.groovy
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections
+
+import org.junit.Test
+
+import spock.lang.Specification
+
+class ListBackedFileSetTest extends Specification {
+
+    public void hasUsefulDisplayName() {
+        def testFile = new File('test-file')
+        def testFile2 = new File('test-file2')
+
+        expect:
+        new ListBackedFileSet().displayName == "empty file collection"
+        new ListBackedFileSet(testFile).displayName == "file '$testFile'"
+        new ListBackedFileSet(testFile, testFile2).displayName == "files '$testFile', '$testFile2'"
+    }
+
+    @Test
+    public void containsSpecifiedFiles() {
+        def testFile = new File('test-file')
+
+        expect:
+        new ListBackedFileSet(testFile).files == [testFile] as Set
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/MapFileTreeTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/MapFileTreeTest.java
new file mode 100644
index 0000000..92e4809
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/MapFileTreeTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.collections;
+
+import groovy.lang.Closure;
+import static org.gradle.api.file.FileVisitorUtil.*;
+import static org.gradle.api.tasks.AntBuilderAwareUtil.*;
+import org.gradle.util.TestFile;
+import org.gradle.util.HelperUtil;
+import org.gradle.util.TemporaryFolder;
+import static org.gradle.util.WrapUtil.*;
+import static org.hamcrest.Matchers.*;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.List;
+
+public class MapFileTreeTest {
+    @Rule
+    public final TemporaryFolder tmpDir = new TemporaryFolder();
+    private TestFile rootDir = tmpDir.getDir();
+    private final MapFileTree tree = new MapFileTree(rootDir);
+
+    @Test
+    public void isEmptyByDefault() {
+        List<String> emptyList = toList();
+        assertVisits(tree, emptyList, emptyList);
+        assertSetContainsForAllTypes(tree, emptyList);
+    }
+    
+    @Test
+    public void canAddAnElementUsingAClosureToGeneratedContent() {
+        Closure closure = HelperUtil.toClosure("{it.write('content'.getBytes())}");
+        tree.add("path/file.txt", closure);
+
+        assertVisits(tree, toList("path/file.txt"), toList("path"));
+        assertSetContainsForAllTypes(tree, toList("path/file.txt"));
+
+        rootDir.file("path").assertIsDir();
+        rootDir.file("path/file.txt").assertContents(equalTo("content"));
+    }
+
+    @Test
+    public void canAddMultipleElementsInDifferentDirs() {
+        Closure closure = HelperUtil.toClosure("{it.write('content'.getBytes())}");
+        tree.add("path/file.txt", closure);
+        tree.add("file.txt", closure);
+        tree.add("path/subdir/file.txt", closure);
+
+        assertVisits(tree, toList("path/file.txt", "file.txt", "path/subdir/file.txt"), toList("path", "path/subdir"));
+        assertSetContainsForAllTypes(tree, toList("path/file.txt", "file.txt", "path/subdir/file.txt"));
+    }
+
+    @Test
+    public void canStopVisitingElements() {
+        Closure closure = HelperUtil.toClosure("{it.write('content'.getBytes())}");
+        tree.add("path/file.txt", closure);
+        tree.add("file.txt", closure);
+        assertCanStopVisiting(tree);
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/SimpleFileCollectionTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/SimpleFileCollectionTest.groovy
new file mode 100644
index 0000000..eb28970
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/SimpleFileCollectionTest.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.internal.file.collections
+
+import spock.lang.Specification
+
+class SimpleFileCollectionTest extends Specification {
+    def collectionContainsFixedSetOfFiles() {
+        File file1 = new File('file1')
+        File file2 = new File('file2')
+
+        expect:
+        SimpleFileCollection collection = new SimpleFileCollection(file1, file2)
+        collection.files as List == [file1, file2]
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/SingletonFileTreeTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/SingletonFileTreeTest.groovy
new file mode 100644
index 0000000..89f8b5c
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/SingletonFileTreeTest.groovy
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collections
+
+import spock.lang.Specification
+import org.gradle.api.file.FileVisitor
+import org.gradle.api.file.FileVisitDetails
+
+class SingletonFileTreeTest extends Specification {
+    def hasUsefulDisplayName() {
+        File f = new File('test-file')
+        SingletonFileTree tree = new SingletonFileTree(f)
+
+        expect:
+        tree.displayName == "file '$f'"
+    }
+    
+    def visitsFileAsChildOfRoot() {
+        FileVisitor visitor = Mock()
+        File f = new File('test-file')
+        SingletonFileTree tree = new SingletonFileTree(f)
+
+        when:
+        tree.visit(visitor)
+
+        then:
+        1 * visitor.visitFile(!null) >> { FileVisitDetails details ->
+            assert details.file == f
+            assert details.path == 'test-file'
+        }
+        0 * visitor._
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/CopySpecImplTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/CopySpecImplTest.groovy
index d4cf3d5..253d525 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/CopySpecImplTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/CopySpecImplTest.groovy
@@ -1,342 +1,360 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.apache.tools.ant.filters.HeadFilter
-import org.apache.tools.ant.filters.StripJavaComments
-import org.gradle.api.file.RelativePath
-import org.gradle.util.TestFile
-import org.gradle.util.JUnit4GroovyMockery
-import org.gradle.util.TemporaryFolder
-import org.jmock.integration.junit4.JMock
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
-import org.apache.tools.zip.UnixStat
-import org.gradle.api.specs.Spec
-import org.gradle.api.tasks.util.PatternSet
-import org.gradle.api.file.FileTree
-import org.gradle.api.Action
-import org.gradle.api.internal.file.FileResolver
-import org.gradle.api.file.CopySpec
-
- at RunWith(JMock)
-public class CopySpecImplTest {
-
-    @Rule public TemporaryFolder testDir = new TemporaryFolder();
-    private TestFile baseFile = testDir.dir
-    private final JUnit4GroovyMockery context = new JUnit4GroovyMockery();
-    private final FileResolver fileResolver = context.mock(FileResolver);
-    private final CopySpecImpl spec = new CopySpecImpl(fileResolver)
-
-    private List<String> getTestSourceFileNames() {
-        ['first', 'second']
-    }
-
-    private List<File> getAbsoluteTestSources() {
-        testSourceFileNames.collect { new File(baseFile, it) }
-    }
-
-    @Test public void testAbsoluteFromList() {
-        List<File> sources = getAbsoluteTestSources();
-        spec.from(sources);
-        assertEquals([sources], spec.sourcePaths as List);
-    }
-
-    @Test public void testFromArray() {
-        List<File> sources = getAbsoluteTestSources();
-        spec.from(sources as File[]);
-        assertEquals(sources, spec.sourcePaths as List);
-    }
-
-    @Test public void testSourceWithClosure() {
-        CopySpecImpl child = spec.from('source') {
-        }
-
-        assertThat(child, not(sameInstance(spec)))
-        assertEquals(['source'], child.sourcePaths as List);
-    }
-
-    @Test public void testMultipleSourcesWithClosure() {
-        CopySpecImpl child = spec.from(['source1', 'source2']) {
-        }
-
-        assertThat(child, not(sameInstance(spec)))
-        assertEquals(['source1', 'source2'], child.sourcePaths.flatten() as List);
-    }
-
-    @Test public void testDefaultDestinationPathForRootSpec() {
-        assertThat(spec.destPath, equalTo(new RelativePath(false)))
-    }
-
-    @Test public void testInto() {
-        spec.into 'spec'
-        assertThat(spec.destPath, equalTo(new RelativePath(false, 'spec')))
-        spec.into '/'
-        assertThat(spec.destPath, equalTo(new RelativePath(false)))
-    }
-
-    @Test public void testWithSpec() {
-        CopySpecImpl other1 = new CopySpecImpl(fileResolver)
-        CopySpecImpl other2 = new CopySpecImpl(fileResolver)
-
-        spec.with other1, other2
-        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)
-
-        spec.with source
-        assertTrue(spec.sourcePaths.empty)
-        assertThat(spec.childSpecs.size(), equalTo(1))
-    }
-
-    @Test public void testWithSpecInheritsDestinationPathFromParent() {
-        CopySpecImpl other = new CopySpecImpl(fileResolver)
-        other.into 'other'
-
-        spec.into 'spec'
-        spec.with other
-
-        ReadableCopySpec child = spec.childSpecs[0]
-        assertThat(child.destPath, equalTo(new RelativePath(false, 'spec', 'other')))
-    }
-
-    @Test public void testDestinationWithClosure() {
-        CopySpecImpl child = spec.into('target') {
-        }
-
-        assertThat(child, not(sameInstance(spec)))
-        assertThat(child.destPath, equalTo(new RelativePath(false, 'target')))
-    }
-
-    @Test public void testGetAllSpecsReturnsBreadthwiseTraverseOfSpecs() {
-        CopySpecImpl child = spec.into('somedir') { }
-        CopySpecImpl grandchild = child.into('somedir') { }
-        CopySpecImpl child2 = spec.into('somedir') { }
-
-        assertThat(spec.allSpecs, equalTo([spec, child, grandchild, child2]))
-    }
-
-    @Test public void testRootSpecHasRootPathAsDestination() {
-        assertThat(spec.destPath, equalTo(new RelativePath(false)))
-    }
-
-    @Test public void testChildSpecResolvesIntoArgRelativeToParentDestinationDir() {
-        CopySpecImpl child = spec.from('somedir') { into 'child' }
-        assertThat(child.destPath, equalTo(new RelativePath(false, 'child')))
-
-        CopySpecImpl grandchild = child.from('somedir') { into 'grandchild'}
-        assertThat(grandchild.destPath, equalTo(new RelativePath(false, 'child', 'grandchild')))
-
-        grandchild.into '/grandchild'
-        assertThat(grandchild.destPath, equalTo(new RelativePath(false, 'grandchild')))
-    }
-
-    @Test public void testChildSpecUsesParentDestinationPathAsDefault() {
-        CopySpecImpl child = spec.from('somedir') { }
-        assertThat(child.destPath, equalTo(spec.destPath))
-
-        child.into 'child'
-
-        CopySpecImpl grandchild = child.from('somedir') { }
-        assertThat(grandchild.destPath, equalTo(child.destPath))
-    }
-
-    @Test public void testSourceIsFilteredTreeOfSources() {
-        spec.from 'a'
-        spec.from 'b'
-
-        def filteredTree = context.mock(FileTree, 'filtered')
-
-        context.checking {
-            one(fileResolver).resolveFilesAsTree(['a', 'b'] as Set)
-            def tree = context.mock(FileTree, 'source')
-            will(returnValue(tree))
-            one(tree).matching(withParam(equalTo(spec.patternSet)))
-            will(returnValue(filteredTree))
-        }
-
-        assertThat(spec.source, sameInstance(filteredTree))
-    }
-
-    @Test public void testChildUsesPatternsFromParent() {
-        CopySpecImpl child = spec.from('dir') {}
-        Spec specInclude = [:] as Spec
-        Spec specExclude = [:] as Spec
-        Spec childInclude = [:] as Spec
-        Spec childExclude = [:] as Spec
-
-        spec.include('parent-include')
-        spec.exclude('parent-exclude')
-        spec.include(specInclude)
-        spec.exclude(specExclude)
-        child.include('child-include')
-        child.exclude('child-exclude')
-        child.include(childInclude)
-        child.exclude(childExclude)
-
-        PatternSet patterns = child.patternSet
-        assertThat(patterns.includes, equalTo(['parent-include', 'child-include'] as Set))
-        assertThat(patterns.excludes, equalTo(['parent-exclude', 'child-exclude'] as Set))
-        assertThat(patterns.includeSpecs, equalTo([specInclude, childInclude] as Set))
-        assertThat(patterns.excludeSpecs, equalTo([specExclude, childExclude] as Set))
-    }
-
-    @Test public void testChildUsesParentPatternsAsDefault() {
-        CopySpecImpl child = spec.from('dir') {}
-        Spec specInclude = [:] as Spec
-        Spec specExclude = [:] as Spec
-
-        spec.include('parent-include')
-        spec.exclude('parent-exclude')
-        spec.include(specInclude)
-        spec.exclude(specExclude)
-
-        PatternSet patterns = child.patternSet
-        assertThat(patterns.includes, equalTo(['parent-include'] as Set))
-        assertThat(patterns.excludes, equalTo(['parent-exclude'] as Set))
-        assertThat(patterns.includeSpecs, equalTo([specInclude] as Set))
-        assertThat(patterns.excludeSpecs, equalTo([specExclude] as Set))
-    }
-
-    @Test public void testChildUsesCaseSensitiveFlagFromParentAsDefault() {
-        CopySpecImpl child = spec.from('dir') {}
-        assertTrue(child.caseSensitive)
-        assertTrue(child.patternSet.caseSensitive)
-
-        spec.caseSensitive = false
-        assertFalse(child.caseSensitive)
-        assertFalse(child.patternSet.caseSensitive)
-
-        child.caseSensitive = true
-        assertTrue(child.caseSensitive)
-        assertTrue(child.patternSet.caseSensitive)
-    }
-
-    @Test public void testNoArgFilter() {
-        spec.filter(StripJavaComments)
-        assertThat(spec.allCopyActions.size(), equalTo(1))
-    }
-
-    @Test public void testArgFilter() {
-        spec.filter(HeadFilter, lines: 15, skip: 2)
-        assertThat(spec.allCopyActions.size(), equalTo(1))
-    }
-
-    @Test public void testExpand() {
-        spec.expand(version: '1.2', skip: 2)
-        assertThat(spec.allCopyActions.size(), equalTo(1))
-    }
-
-    @Test public void testTwoFilters() {
-        spec.filter(StripJavaComments)
-        spec.filter(HeadFilter, lines: 15, skip: 2)
-
-        assertThat(spec.allCopyActions.size(), equalTo(2))
-    }
-
-    @Test public void testAddsStringNameTransformerToActions() {
-        spec.rename("regexp", "replacement")
-
-        assertThat(spec.allCopyActions.size(), equalTo(1))
-        assertThat(spec.allCopyActions[0], instanceOf(RenamingCopyAction))
-        assertThat(spec.allCopyActions[0].transformer, instanceOf(RegExpNameMapper))
-    }
-
-    @Test public void testAddsPatternNameTransformerToActions() {
-        spec.rename(/regexp/, "replacement")
-
-        assertThat(spec.allCopyActions.size(), equalTo(1))
-        assertThat(spec.allCopyActions[0], instanceOf(RenamingCopyAction))
-        assertThat(spec.allCopyActions[0].transformer, instanceOf(RegExpNameMapper))
-    }
-
-    @Test public void testAddsClosureToActions() {
-        spec.rename {}
-
-        assertThat(spec.allCopyActions.size(), equalTo(1))
-        assertThat(spec.allCopyActions[0], instanceOf(RenamingCopyAction))
-    }
-
-    @Test public void testAddAction() {
-        def action = context.mock(Action)
-        spec.eachFile(action)
-
-        assertThat(spec.allCopyActions, equalTo([action]))
-    }
-
-    @Test public void testAddActionAsClosure() {
-        def action = {}
-        spec.eachFile(action)
-
-        assertThat(spec.allCopyActions.size(), equalTo(1))
-    }
-
-    @Test public void testSpecInheritsActionsFromParent() {
-        Action parentAction = context.mock(Action, 'parent')
-        Action childAction = context.mock(Action, 'child')
-
-        spec.eachFile parentAction
-        CopySpecImpl childSpec = spec.from('src') {
-            eachFile childAction
-        }
-
-        assertThat(childSpec.allCopyActions, equalTo([parentAction, childAction]))
-    }
-
-    @Test public void testDefaultPermissions() {
-        org.junit.Assert.assertEquals(UnixStat.DEFAULT_FILE_PERM, spec.fileMode)
-        org.junit.Assert.assertEquals(UnixStat.DEFAULT_DIR_PERM, spec.dirMode)
-    }
-
-    @Test public void testInheritsPermissionsFromParent() {
-        spec.fileMode = 0x1
-        spec.dirMode = 0x2
-
-        CopySpecImpl child = spec.from('src') { }
-        org.junit.Assert.assertEquals(0x1, child.fileMode)
-        org.junit.Assert.assertEquals(0x2, child.dirMode)
-    }
-
-    @Test public void testHasNoSourceByDefault() {
-        assertFalse(spec.hasSource())
-    }
-
-    @Test public void testHasSourceWhenSpecHasSource() {
-        spec.from 'source'
-        assertTrue(spec.hasSource())
-    }
-
-    @Test public void testHasSourceWhenChildSpecHasSource() {
-        spec.from('source') {}
-        assertTrue(spec.hasSource())
-    }
-}
-
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.apache.tools.ant.filters.HeadFilter
+import org.apache.tools.ant.filters.StripJavaComments
+import org.gradle.api.file.RelativePath
+import org.gradle.util.TestFile
+import org.gradle.util.JUnit4GroovyMockery
+import org.gradle.util.TemporaryFolder
+import org.jmock.integration.junit4.JMock
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import static org.hamcrest.Matchers.*
+import static org.junit.Assert.*
+import org.apache.tools.zip.UnixStat
+import org.gradle.api.specs.Spec
+import org.gradle.api.tasks.util.PatternSet
+import org.gradle.api.file.FileTree
+import org.gradle.api.Action
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.api.file.CopySpec
+
+ at RunWith(JMock)
+public class CopySpecImplTest {
+
+    @Rule public TemporaryFolder testDir = new TemporaryFolder();
+    private TestFile baseFile = testDir.dir
+    private final JUnit4GroovyMockery context = new JUnit4GroovyMockery();
+    private final FileResolver fileResolver = context.mock(FileResolver);
+    private final CopySpecImpl spec = new CopySpecImpl(fileResolver)
+
+    private List<String> getTestSourceFileNames() {
+        ['first', 'second']
+    }
+
+    private List<File> getAbsoluteTestSources() {
+        testSourceFileNames.collect { new File(baseFile, it) }
+    }
+
+    @Test public void testAbsoluteFromList() {
+        List<File> sources = getAbsoluteTestSources();
+        spec.from(sources);
+        assertEquals([sources], spec.sourcePaths as List);
+    }
+
+    @Test public void testFromArray() {
+        List<File> sources = getAbsoluteTestSources();
+        spec.from(sources as File[]);
+        assertEquals(sources, spec.sourcePaths as List);
+    }
+
+    @Test public void testSourceWithClosure() {
+        CopySpecImpl child = spec.from('source') {
+        }
+
+        assertThat(child, not(sameInstance(spec)))
+        assertEquals(['source'], child.sourcePaths as List);
+    }
+
+    @Test public void testMultipleSourcesWithClosure() {
+        CopySpecImpl child = spec.from(['source1', 'source2']) {
+        }
+
+        assertThat(child, not(sameInstance(spec)))
+        assertEquals(['source1', 'source2'], child.sourcePaths.flatten() as List);
+    }
+
+    @Test public void testDefaultDestinationPathForRootSpec() {
+        assertThat(spec.destPath, equalTo(new RelativePath(false)))
+    }
+
+    @Test public void testInto() {
+        spec.into 'spec'
+        assertThat(spec.destPath, equalTo(new RelativePath(false, 'spec')))
+        spec.into '/'
+        assertThat(spec.destPath, equalTo(new RelativePath(false)))
+    }
+
+    @Test public void testIntoWithAClosure() {
+        spec.into { 'spec' }
+        assertThat(spec.destPath, equalTo(new RelativePath(false, 'spec')))
+        spec.into { return { 'spec' } }
+        assertThat(spec.destPath, equalTo(new RelativePath(false, 'spec')))
+    }
+
+    @Test public void testWithSpec() {
+        CopySpecImpl other1 = new CopySpecImpl(fileResolver)
+        CopySpecImpl other2 = new CopySpecImpl(fileResolver)
+
+        spec.with other1, other2
+        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)
+
+        spec.with source
+        assertTrue(spec.sourcePaths.empty)
+        assertThat(spec.childSpecs.size(), equalTo(1))
+    }
+
+    @Test public void testWithSpecInheritsDestinationPathFromParent() {
+        CopySpecImpl other = new CopySpecImpl(fileResolver)
+        other.into 'other'
+
+        spec.into 'spec'
+        spec.with other
+
+        ReadableCopySpec child = spec.childSpecs[0]
+        assertThat(child.destPath, equalTo(new RelativePath(false, 'spec', 'other')))
+    }
+
+    @Test public void testDestinationWithClosure() {
+        CopySpecImpl child = spec.into('target') {
+        }
+
+        assertThat(child, not(sameInstance(spec)))
+        assertThat(child.destPath, equalTo(new RelativePath(false, 'target')))
+    }
+
+    @Test public void testGetAllSpecsReturnsBreadthwiseTraverseOfSpecs() {
+        CopySpecImpl child = spec.into('somedir') { }
+        CopySpecImpl grandchild = child.into('somedir') { }
+        CopySpecImpl child2 = spec.into('somedir') { }
+
+        assertThat(spec.allSpecs, equalTo([spec, child, grandchild, child2]))
+    }
+
+    @Test public void testRootSpecHasRootPathAsDestination() {
+        assertThat(spec.destPath, equalTo(new RelativePath(false)))
+    }
+
+    @Test public void testChildSpecResolvesIntoArgRelativeToParentDestinationDir() {
+        CopySpecImpl child = spec.from('somedir') { into 'child' }
+        assertThat(child.destPath, equalTo(new RelativePath(false, 'child')))
+
+        CopySpecImpl grandchild = child.from('somedir') { into 'grandchild'}
+        assertThat(grandchild.destPath, equalTo(new RelativePath(false, 'child', 'grandchild')))
+
+        grandchild.into '/grandchild'
+        assertThat(grandchild.destPath, equalTo(new RelativePath(false, 'grandchild')))
+    }
+
+    @Test public void testChildSpecUsesParentDestinationPathAsDefault() {
+        CopySpecImpl child = spec.from('somedir') { }
+        assertThat(child.destPath, equalTo(spec.destPath))
+
+        child.into 'child'
+
+        CopySpecImpl grandchild = child.from('somedir') { }
+        assertThat(grandchild.destPath, equalTo(child.destPath))
+    }
+
+    @Test public void testSourceIsFilteredTreeOfSources() {
+        spec.from 'a'
+        spec.from 'b'
+
+        def filteredTree = context.mock(FileTree, 'filtered')
+
+        context.checking {
+            one(fileResolver).resolveFilesAsTree(['a', 'b'] as Set)
+            def tree = context.mock(FileTree, 'source')
+            will(returnValue(tree))
+            one(tree).matching(withParam(equalTo(spec.patternSet)))
+            will(returnValue(filteredTree))
+        }
+
+        assertThat(spec.source, sameInstance(filteredTree))
+    }
+
+    @Test public void testChildUsesPatternsFromParent() {
+        CopySpecImpl child = spec.from('dir') {}
+        Spec specInclude = [:] as Spec
+        Spec specExclude = [:] as Spec
+        Spec childInclude = [:] as Spec
+        Spec childExclude = [:] as Spec
+
+        spec.include('parent-include')
+        spec.exclude('parent-exclude')
+        spec.include(specInclude)
+        spec.exclude(specExclude)
+        child.include('child-include')
+        child.exclude('child-exclude')
+        child.include(childInclude)
+        child.exclude(childExclude)
+
+        PatternSet patterns = child.patternSet
+        assertThat(patterns.includes, equalTo(['parent-include', 'child-include'] as Set))
+        assertThat(patterns.excludes, equalTo(['parent-exclude', 'child-exclude'] as Set))
+        assertThat(patterns.includeSpecs, equalTo([specInclude, childInclude] as Set))
+        assertThat(patterns.excludeSpecs, equalTo([specExclude, childExclude] as Set))
+    }
+
+    @Test public void testChildUsesParentPatternsAsDefault() {
+        CopySpecImpl child = spec.from('dir') {}
+        Spec specInclude = [:] as Spec
+        Spec specExclude = [:] as Spec
+
+        spec.include('parent-include')
+        spec.exclude('parent-exclude')
+        spec.include(specInclude)
+        spec.exclude(specExclude)
+
+        PatternSet patterns = child.patternSet
+        assertThat(patterns.includes, equalTo(['parent-include'] as Set))
+        assertThat(patterns.excludes, equalTo(['parent-exclude'] as Set))
+        assertThat(patterns.includeSpecs, equalTo([specInclude] as Set))
+        assertThat(patterns.excludeSpecs, equalTo([specExclude] as Set))
+    }
+
+    @Test public void testChildUsesCaseSensitiveFlagFromParentAsDefault() {
+        CopySpecImpl child = spec.from('dir') {}
+        assertTrue(child.caseSensitive)
+        assertTrue(child.patternSet.caseSensitive)
+
+        spec.caseSensitive = false
+        assertFalse(child.caseSensitive)
+        assertFalse(child.patternSet.caseSensitive)
+
+        child.caseSensitive = true
+        assertTrue(child.caseSensitive)
+        assertTrue(child.patternSet.caseSensitive)
+    }
+
+    @Test public void testChildUsesIncludeEmptyDirsFlagFromParentAsDefault() {
+        def child = spec.from('dir') {}
+        assert child.includeEmptyDirs
+
+        spec.includeEmptyDirs = false
+        assert !child.includeEmptyDirs
+
+        child.includeEmptyDirs = true
+        assert child.includeEmptyDirs
+    }
+
+    @Test public void testNoArgFilter() {
+        spec.filter(StripJavaComments)
+        assertThat(spec.allCopyActions.size(), equalTo(1))
+    }
+
+    @Test public void testArgFilter() {
+        spec.filter(HeadFilter, lines: 15, skip: 2)
+        assertThat(spec.allCopyActions.size(), equalTo(1))
+    }
+
+    @Test public void testExpand() {
+        spec.expand(version: '1.2', skip: 2)
+        assertThat(spec.allCopyActions.size(), equalTo(1))
+    }
+
+    @Test public void testTwoFilters() {
+        spec.filter(StripJavaComments)
+        spec.filter(HeadFilter, lines: 15, skip: 2)
+
+        assertThat(spec.allCopyActions.size(), equalTo(2))
+    }
+
+    @Test public void testAddsStringNameTransformerToActions() {
+        spec.rename("regexp", "replacement")
+
+        assertThat(spec.allCopyActions.size(), equalTo(1))
+        assertThat(spec.allCopyActions[0], instanceOf(RenamingCopyAction))
+        assertThat(spec.allCopyActions[0].transformer, instanceOf(RegExpNameMapper))
+    }
+
+    @Test public void testAddsPatternNameTransformerToActions() {
+        spec.rename(/regexp/, "replacement")
+
+        assertThat(spec.allCopyActions.size(), equalTo(1))
+        assertThat(spec.allCopyActions[0], instanceOf(RenamingCopyAction))
+        assertThat(spec.allCopyActions[0].transformer, instanceOf(RegExpNameMapper))
+    }
+
+    @Test public void testAddsClosureToActions() {
+        spec.rename {}
+
+        assertThat(spec.allCopyActions.size(), equalTo(1))
+        assertThat(spec.allCopyActions[0], instanceOf(RenamingCopyAction))
+    }
+
+    @Test public void testAddAction() {
+        def action = context.mock(Action)
+        spec.eachFile(action)
+
+        assertThat(spec.allCopyActions, equalTo([action]))
+    }
+
+    @Test public void testAddActionAsClosure() {
+        def action = {}
+        spec.eachFile(action)
+
+        assertThat(spec.allCopyActions.size(), equalTo(1))
+    }
+
+    @Test public void testSpecInheritsActionsFromParent() {
+        Action parentAction = context.mock(Action, 'parent')
+        Action childAction = context.mock(Action, 'child')
+
+        spec.eachFile parentAction
+        CopySpecImpl childSpec = spec.from('src') {
+            eachFile childAction
+        }
+
+        assertThat(childSpec.allCopyActions, equalTo([parentAction, childAction]))
+    }
+
+    @Test public void testDefaultPermissions() {
+        org.junit.Assert.assertEquals(UnixStat.DEFAULT_FILE_PERM, spec.fileMode)
+        org.junit.Assert.assertEquals(UnixStat.DEFAULT_DIR_PERM, spec.dirMode)
+    }
+
+    @Test public void testInheritsPermissionsFromParent() {
+        spec.fileMode = 0x1
+        spec.dirMode = 0x2
+
+        CopySpecImpl child = spec.from('src') { }
+        org.junit.Assert.assertEquals(0x1, child.fileMode)
+        org.junit.Assert.assertEquals(0x2, child.dirMode)
+    }
+
+    @Test public void testHasNoSourceByDefault() {
+        assertFalse(spec.hasSource())
+    }
+
+    @Test public void testHasSourceWhenSpecHasSource() {
+        spec.from 'source'
+        assertTrue(spec.hasSource())
+    }
+
+    @Test public void testHasSourceWhenChildSpecHasSource() {
+        spec.from('source') {}
+        assertTrue(spec.hasSource())
+    }
+}
+
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/FileCopySpecVisitorTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/FileCopySpecVisitorTest.java
index 83468cf..8f1f1a1 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/FileCopySpecVisitorTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/FileCopySpecVisitorTest.java
@@ -19,7 +19,6 @@ package org.gradle.api.internal.file.copy;
 import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.file.FileVisitDetails;
 import org.gradle.api.file.RelativePath;
-import org.gradle.util.TestFile;
 import org.gradle.util.TemporaryFolder;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
@@ -38,7 +37,6 @@ import static org.junit.Assert.*;
 @RunWith(JMock.class)
 public class FileCopySpecVisitorTest {
     private File destDir;
-    private TestFile sourceDir;
     private final JUnit4Mockery context = new JUnit4Mockery();
     private final FileCopySpecVisitor visitor = new FileCopySpecVisitor();
     @Rule
@@ -47,19 +45,17 @@ public class FileCopySpecVisitorTest {
     @Before
     public void setUp() throws IOException {
         destDir = tmpDir.getDir().file("dest");
-        sourceDir = tmpDir.getDir().file("src").createDir();
     }
 
     @Test
     public void plainCopy() {
         visitor.startVisit(action(destDir));
 
-        visitor.visitDir(file(new RelativePath(false)));
+        visitor.visitDir(file(new RelativePath(false), destDir));
 
         visitor.visitFile(file(new RelativePath(true, "rootfile.txt"), new File(destDir, "rootfile.txt")));
 
-        RelativePath subDirPath = new RelativePath(false, "subdir");
-        visitor.visitDir(file(subDirPath));
+        visitor.visitDir(file(new RelativePath(false, "subdir"), new File(destDir, "subdir")));
 
         visitor.visitFile(file(new RelativePath(true, "subdir", "anotherfile.txt"), new File(destDir, "subdir/anotherfile.txt")));
     }
@@ -83,18 +79,11 @@ public class FileCopySpecVisitorTest {
         return action;
     }
 
-    private FileVisitDetails file(final RelativePath relativePath) {
+    private FileVisitDetails file(final RelativePath relativePath, final File targetFile) {
         final FileVisitDetails details = context.mock(FileVisitDetails.class, relativePath.getPathString());
         context.checking(new Expectations(){{
             allowing(details).getRelativePath();
             will(returnValue(relativePath));
-        }});
-        return details;
-    }
-
-    private FileVisitDetails file(final RelativePath relativePath, final File targetFile) {
-        final FileVisitDetails details = file(relativePath);
-        context.checking(new Expectations(){{
             one(details).copyTo(targetFile);
         }});
         return details;
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/LineFilterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/LineFilterTest.groovy
index 8f3bc87..23d5215 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/LineFilterTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/LineFilterTest.groovy
@@ -1,74 +1,83 @@
-/*
- * 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.junit.Test
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-public class LineFilterTest {
-    @Test public void testEmptyInput() {
-        Reader input = new StringReader("");
-        int lineCount = 1;
-        LineFilter filter = new LineFilter(input, { "${lineCount++} - $it" as String })
-
-        assertThat(filter.text, equalTo(""))
-    }
-
-    @Test public void testEmptyLinesWithTrailingEOL() {
-        Reader input = new StringReader("\n\n");
-        int lineCount = 1;
-        LineFilter filter = new LineFilter(input, { "${lineCount++} - $it" as String })
-
-        assertThat(filter.text, equalTo(lines("1 - ", "2 - ", "")))
-    }
-    
-    @Test public void testSingleLine() {
-        Reader input = new StringReader("one");
-        int lineCount = 1;
-        LineFilter filter = new LineFilter(input, { "${lineCount++} - $it" as String })
-
-        assertThat(filter.text, equalTo("1 - one"))
-    }
-    
-    @Test public void testCRLFWithTrailingEOL() {
-        Reader input = new StringReader("one\r\ntwo\r\nthree\r\n");
-        int lineCount = 1;
-        LineFilter filter = new LineFilter(input,  { "${lineCount++} - $it" as String })
-
-        assertThat(filter.text, equalTo(lines("1 - one", "2 - two", "3 - three", "")))
-    }
-
-    @Test public void testLfWithNoTrailingEOL() {
-        Reader input = new StringReader("one\ntwo\nthree");
-        int lineCount = 1;
-        LineFilter filter = new LineFilter(input,  { "${lineCount++} - $it" as String })
-
-        assertThat(filter.text, equalTo(lines("1 - one", "2 - two", "3 - three")))
-    }
-    
-    @Test public void testCRWithNoTrailingEOL() {
-        Reader input = new StringReader("one\rtwo\rthree");
-        int lineCount = 1;
-        LineFilter filter = new LineFilter(input, { "${lineCount++} - $it" as String })
-
-        assertThat(filter.text, equalTo(lines("1 - one", "2 - two", "3 - three")))
-    }
-
-    private String lines(String ... lines) {
-        return (lines as List).join(System.getProperty('line.separator'))
-    }
+/*
+ * 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.junit.Test
+import org.gradle.util.SystemProperties
+
+import static org.hamcrest.Matchers.*
+import static org.junit.Assert.*
+
+class LineFilterTest {
+    @Test void testEmptyInput() {
+        def input = new StringReader("")
+        def lineCount = 1
+        def filter = new LineFilter(input, { "${lineCount++} - $it" as String })
+
+        assertThat(filter.text, equalTo(""))
+    }
+
+    @Test void testEmptyLinesWithTrailingEOL() {
+        def input = new StringReader("\n\n")
+        def lineCount = 1
+        def filter = new LineFilter(input, { "${lineCount++} - $it" as String })
+
+        assertThat(filter.text, equalTo(lines("1 - ", "2 - ", "")))
+    }
+    
+    @Test void testSingleLine() {
+        def input = new StringReader("one")
+        def lineCount = 1
+        def filter = new LineFilter(input, { "${lineCount++} - $it" as String })
+
+        assertThat(filter.text, equalTo("1 - one"))
+    }
+
+    @Test void testWithEmptyReplacementString() {
+        def input = new StringReader("one")
+        def filter = new LineFilter(input, {""})
+
+        assertThat(filter.text, equalTo(""))
+    }
+    
+    @Test void testCRLFWithTrailingEOL() {
+        def input = new StringReader("one\r\ntwo\r\nthree\r\n")
+        def lineCount = 1
+        def filter = new LineFilter(input,  { "${lineCount++} - $it" as String })
+
+        assertThat(filter.text, equalTo(lines("1 - one", "2 - two", "3 - three", "")))
+    }
+
+    @Test void testLfWithNoTrailingEOL() {
+        def input = new StringReader("one\ntwo\nthree")
+        def lineCount = 1
+        def filter = new LineFilter(input,  { "${lineCount++} - $it" as String })
+
+        assertThat(filter.text, equalTo(lines("1 - one", "2 - two", "3 - three")))
+    }
+    
+    @Test void testCRWithNoTrailingEOL() {
+        def input = new StringReader("one\rtwo\rthree")
+        def lineCount = 1
+        def filter = new LineFilter(input, { "${lineCount++} - $it" as String })
+
+        assertThat(filter.text, equalTo(lines("1 - one", "2 - two", "3 - three")))
+    }
+
+    private String lines(String ... lines) {
+        (lines as List).join(SystemProperties.lineSeparator)
+    }
 }
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/NormalizingCopySpecVisitorTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/NormalizingCopySpecVisitorTest.java
index d6db593..53e3889 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/NormalizingCopySpecVisitorTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/NormalizingCopySpecVisitorTest.java
@@ -32,17 +32,29 @@ public class NormalizingCopySpecVisitorTest {
     private final JUnit4Mockery context = new JUnit4Mockery();
     private final CopySpecVisitor delegate = context.mock(CopySpecVisitor.class);
     private final NormalizingCopySpecVisitor visitor = new NormalizingCopySpecVisitor(delegate);
+    private final ReadableCopySpec spec = context.mock(ReadableCopySpec.class);
+
+    private void allowGetIncludeEmptyDirs() {
+        context.checking(new Expectations() {{
+            allowing(spec).getIncludeEmptyDirs();
+            will(returnValue(true));
+        }});
+    }
 
     @Test
     public void doesNotVisitADirectoryWhichHasBeenVisitedBefore() {
         final FileVisitDetails details = file("dir");
         final FileVisitDetails file = file("dir/file");
 
+        allowGetIncludeEmptyDirs();
+
         context.checking(new Expectations() {{
+            one(delegate).visitSpec(spec);
             one(delegate).visitDir(details);
             one(delegate).visitFile(file);
         }});
 
+        visitor.visitSpec(spec);
         visitor.visitDir(details);
         visitor.visitFile(file);
         visitor.visitDir(details);
@@ -53,6 +65,13 @@ public class NormalizingCopySpecVisitorTest {
         final FileVisitDetails dir = file("dir");
         final FileVisitDetails file = file("dir/file");
 
+        allowGetIncludeEmptyDirs();
+
+        context.checking(new Expectations() {{
+            one(delegate).visitSpec(spec);
+        }});
+
+        visitor.visitSpec(spec);
         visitor.visitDir(dir);
 
         context.checking(new Expectations() {{
@@ -69,6 +88,13 @@ public class NormalizingCopySpecVisitorTest {
         final FileVisitDetails subdir = file("dir/sub");
         final FileVisitDetails file = file("dir/sub/file");
 
+        allowGetIncludeEmptyDirs();
+
+        context.checking(new Expectations() {{
+            one(delegate).visitSpec(spec);
+        }});
+
+        visitor.visitSpec(spec);
         visitor.visitDir(dir);
         visitor.visitDir(subdir);
 
@@ -86,13 +112,17 @@ public class NormalizingCopySpecVisitorTest {
         final FileVisitDetails dir1 = file("a/b/c");
         final FileVisitDetails file1 = file("a/b/c/file");
 
+        allowGetIncludeEmptyDirs();
+
         context.checking(new Expectations() {{
+            one(delegate).visitSpec(spec);
             one(delegate).visitDir(with(hasPath("a")));
             one(delegate).visitDir(with(hasPath("a/b")));
             one(delegate).visitDir(dir1);
             one(delegate).visitFile(file1);
         }});
 
+        visitor.visitSpec(spec);
         visitor.visitDir(dir1);
         visitor.visitFile(file1);
 
@@ -100,11 +130,13 @@ public class NormalizingCopySpecVisitorTest {
         final FileVisitDetails file2 = file("a/b/d/e/file");
 
         context.checking(new Expectations() {{
+            one(delegate).visitSpec(spec);
             one(delegate).visitDir(with(hasPath("a/b/d")));
             one(delegate).visitDir(dir2);
             one(delegate).visitFile(file2);
         }});
 
+        visitor.visitSpec(spec);
         visitor.visitDir(dir2);
         visitor.visitFile(file2);
     }
@@ -113,18 +145,22 @@ public class NormalizingCopySpecVisitorTest {
     public void visitsFileAncestorsWhichHaveNotBeenVisited() {
         final FileVisitDetails details = file("a/b/c");
 
+        allowGetIncludeEmptyDirs();
+
         context.checking(new Expectations() {{
+            one(delegate).visitSpec(spec);
             one(delegate).visitDir(with(hasPath("a")));
             one(delegate).visitDir(with(hasPath("a/b")));
             one(delegate).visitFile(details);
         }});
 
+        visitor.visitSpec(spec);
         visitor.visitFile(details);
     }
 
     @Test
     public void visitSpecDelegatesToVisitor() {
-        final ReadableCopySpec spec = context.mock(ReadableCopySpec.class);
+        allowGetIncludeEmptyDirs();
 
         context.checking(new Expectations() {{
             one(delegate).visitSpec(spec);
@@ -133,9 +169,42 @@ public class NormalizingCopySpecVisitorTest {
         visitor.visitSpec(spec);
     }
 
+    @Test
+    public void visitsAnEmptyDirectoryIfCorrespondingOptionIsOn() {
+        final FileVisitDetails dir = file("dir");
+
+        context.checking(new Expectations() {{
+            one(spec).getIncludeEmptyDirs();
+            will(returnValue(true));
+            one(delegate).visitSpec(spec);
+            one(delegate).visitDir(dir);
+            one(delegate).endVisit();
+        }});
+
+        visitor.visitSpec(spec);
+        visitor.visitDir(dir);
+        visitor.endVisit();
+    }
+
+    @Test
+    public void doesNotVisitAnEmptyDirectoryIfCorrespondingOptionIsOff() {
+        FileVisitDetails dir = file("dir");
+
+        context.checking(new Expectations() {{
+            one(spec).getIncludeEmptyDirs();
+            will(returnValue(false));
+            one(delegate).visitSpec(spec);
+            one(delegate).endVisit();
+        }});
+
+        visitor.visitSpec(spec);
+        visitor.visitDir(dir);
+        visitor.endVisit();
+    }
+
     private FileVisitDetails file(final String path) {
         final FileVisitDetails details = context.mock(FileVisitDetails.class, path);
-        context.checking(new Expectations(){{
+        context.checking(new Expectations() {{
             allowing(details).getRelativePath();
             will(returnValue(RelativePath.parse(false, path)));
         }});
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/SyncCopySpecVisitorTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/SyncCopySpecVisitorTest.java
index 538e9bf..685c770 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/SyncCopySpecVisitorTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/SyncCopySpecVisitorTest.java
@@ -16,9 +16,11 @@
 package org.gradle.api.internal.file.copy;
 
 import org.gradle.api.file.FileVisitDetails;
+import org.gradle.api.file.FileVisitor;
 import org.gradle.api.file.RelativePath;
-import org.gradle.util.TestFile;
+import org.gradle.api.internal.file.collections.DirectoryFileTree;
 import org.gradle.util.TemporaryFolder;
+import org.gradle.util.TestFile;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.integration.junit4.JUnit4Mockery;
@@ -28,9 +30,12 @@ import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.io.File;
+import java.lang.reflect.Field;
+import java.util.HashSet;
+import java.util.Set;
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertTrue;
 
 @RunWith(JMock.class)
 public class SyncCopySpecVisitorTest {
@@ -53,10 +58,10 @@ public class SyncCopySpecVisitorTest {
     @Test
     public void deletesExtraFilesFromDestinationDirectoryAtTheEndOfVisit() {
         TestFile destDir = tmpDir.createDir("dest");
-        destDir.file("subdir/included.txt").createFile();
-        destDir.file("subdir/extra.txt").createFile();
-        destDir.file("included.txt").createFile();
-        destDir.file("extra.txt").createFile();
+        destDir.createFile("subdir/included.txt");
+        destDir.createFile("subdir/extra.txt");
+        destDir.createFile("included.txt");
+        destDir.createFile("extra.txt");
 
         visitor.startVisit(action(destDir));
         visitor.visitDir(dir("subdir"));
@@ -68,13 +73,32 @@ public class SyncCopySpecVisitorTest {
     }
 
     @Test
-    public void deletesExtraDirectoriesFromDestinationDirectoryAtTheEndOfVisit() {
+    public void deletesExtraDirectoriesFromDestinationDirectoryAtTheEndOfVisit() throws Exception {
         TestFile destDir = tmpDir.createDir("dest");
-        destDir.file("included.txt").createFile();
-        destDir.file("extra/extra.txt").createFile();
+        destDir.createFile("included.txt");
+        destDir.createFile("extra/extra.txt");
 
         visitor.startVisit(action(destDir));
         visitor.visitFile(file("included.txt"));
+
+        // TODO - delete these
+        Field field = SyncCopySpecVisitor.class.getDeclaredField("visited");
+        field.setAccessible(true);
+        Set visited = (Set) field.get(visitor);
+        assert visited.contains(new RelativePath(true, "included.txt"));
+        assert !visited.contains(new RelativePath(true, "extra", "extra.txt"));
+        final Set actual = new HashSet();
+        new DirectoryFileTree(destDir).depthFirst().visit(new FileVisitor() {
+            public void visitDir(FileVisitDetails dirDetails) {
+            }
+
+            public void visitFile(FileVisitDetails fileDetails) {
+                actual.add(fileDetails.getRelativePath());
+            }
+        });
+        assert actual.contains(new RelativePath(true, "included.txt"));
+        assert actual.contains(new RelativePath(true, "extra", "extra.txt"));
+
         visitor.endVisit();
 
         destDir.assertHasDescendants("included.txt");
@@ -83,8 +107,8 @@ public class SyncCopySpecVisitorTest {
     @Test
     public void doesNotDeleteDestDirectoryWhenNothingCopied() {
         TestFile destDir = tmpDir.createDir("dest");
-        destDir.file("extra.txt").createFile();
-        destDir.file("extra/extra.txt").createFile();
+        destDir.createFile("extra.txt");
+        destDir.createFile("extra/extra.txt");
 
         visitor.startVisit(action(destDir));
         visitor.endVisit();
@@ -105,7 +129,7 @@ public class SyncCopySpecVisitorTest {
     @Test
     public void didWorkWhenFilesDeleted() {
         TestFile destDir = tmpDir.createDir("dest");
-        destDir.file("extra.txt").createFile();
+        destDir.createFile("extra.txt");
 
         visitor.startVisit(action(destDir));
         visitor.endVisit();
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/IdePluginTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/IdePluginTest.groovy
deleted file mode 100644
index 0b5cfb4..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/IdePluginTest.groovy
+++ /dev/null
@@ -1,65 +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.plugins
-
-import spock.lang.Specification
-import org.gradle.api.Project
-import org.gradle.util.HelperUtil
-import org.gradle.api.Task
-import org.gradle.api.DefaultTask
-import org.gradle.api.tasks.Delete
-
-class IdePluginTest extends Specification {
-    final Project project = HelperUtil.createRootProject()
-
-    def addsLifecycleTasks() {
-        when:
-        new TestIdePlugin().apply(project)
-
-        then:
-        Task ideTask = project.tasks['testIde']
-        ideTask instanceof DefaultTask
-        ideTask.group == 'IDE'
-
-        Task cleanTask = project.tasks['cleanTestIde']
-        cleanTask instanceof DefaultTask
-        cleanTask.group == 'IDE'
-    }
-
-    def addsWorkerTask() {
-        when:
-        new TestIdePlugin().apply(project)
-
-        then:
-        Task worker = project.tasks['generateXml']
-        Task ideTask = project.tasks['testIde']
-        ideTask.taskDependencies.getDependencies(ideTask) == [worker] as Set
-
-        Task cleaner = project.tasks['cleanGenerateXml']
-        cleaner instanceof Delete
-    }
-}
-
-class TestIdePlugin extends IdePlugin {
-    @Override protected String getLifecycleTaskName() {
-        return 'testIde'
-    }
-
-    @Override protected void onApply(Project target) {
-        def worker = target.task('generateXml')
-        addWorker(worker)
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultProjectTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultProjectTest.groovy
index 85e27b7..beb754d 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultProjectTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultProjectTest.groovy
@@ -61,6 +61,7 @@ import org.gradle.api.*
 import org.gradle.api.internal.*
 import static org.hamcrest.Matchers.*
 import static org.junit.Assert.*
+import org.gradle.api.internal.Factory
 
 /**
  * @author Hans Dockter
@@ -978,10 +979,10 @@ def scriptMethod(Closure closure) {
                         testSubProp = propValue
                     }
         } else {
-            project."$configureMethod"
+            project."$configureMethod"(
             {
                 testSubProp = propValue
-            }
+            })
         }
 
         projectsToCheck.each {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultServiceRegistryTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultServiceRegistryTest.java
index f989126..d8d2e8e 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultServiceRegistryTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultServiceRegistryTest.java
@@ -1,297 +1,297 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.internal.Factory;
-import org.jmock.Expectations;
-import org.jmock.integration.junit4.JMock;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.math.BigDecimal;
-import java.util.Map;
-
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
- at RunWith(JMock.class)
-public class DefaultServiceRegistryTest {
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private final TestRegistry registry = new TestRegistry();
-
-    @Test
-    public void throwsExceptionForUnknownService() {
-        try {
-            registry.get(Map.class);
-            fail();
-        } catch (IllegalArgumentException e) {
-            assertThat(e.getMessage(), equalTo("No service of type Map available in TestRegistry."));
-        }
-    }
-
-    @Test
-    public void delegatesToParentForUnknownService() {
-        final BigDecimal value = BigDecimal.TEN;
-        final ServiceRegistry parent = context.mock(ServiceRegistry.class);
-        TestRegistry registry = new TestRegistry(parent);
-
-        context.checking(new Expectations(){{
-            one(parent).get(BigDecimal.class);
-            will(returnValue(value));
-        }});
-
-        assertThat(registry.get(BigDecimal.class), sameInstance(value));
-    }
-
-    @Test
-    public void throwsExceptionForUnknownParentService() {
-        final ServiceRegistry parent = context.mock(ServiceRegistry.class);
-        TestRegistry registry = new TestRegistry(parent);
-
-        context.checking(new Expectations(){{
-            one(parent).get(Map.class);
-            will(throwException(new DefaultServiceRegistry.UnknownServiceException(Map.class, "fail")));
-        }});
-
-        try {
-            registry.get(Map.class);
-            fail();
-        } catch (IllegalArgumentException e) {
-            assertThat(e.getMessage(), equalTo("No service of type Map available in TestRegistry."));
-        }
-    }
-
-    @Test
-    public void returnsAddedServiceInstance() {
-        BigDecimal value = BigDecimal.TEN;
-        registry.add(BigDecimal.class, value);
-        assertThat(registry.get(BigDecimal.class), sameInstance(value));
-        assertThat(registry.get(Number.class), sameInstance((Object) value));
-    }
-
-    @Test
-    public void createsAndCachesRegisteredServiceInstance() {
-        final BigDecimal value = BigDecimal.TEN;
-        registry.add(new DefaultServiceRegistry.Service(BigDecimal.class) {
-            @Override
-            protected Object create() {
-                return value;
-            }
-        });
-        assertThat(registry.get(BigDecimal.class), sameInstance(value));
-        assertThat(registry.get(Number.class), sameInstance((Object) value));
-    }
-
-    @Test
-    public void usesFactoryMethodToCreateServiceInstance() {
-        assertThat(registry.get(String.class), equalTo("12"));
-        assertThat(registry.get(Integer.class), equalTo(12));
-    }
-
-    @Test
-    public void usesDecoratorMethodToDecorateParentServiceInstance() {
-        final ServiceRegistry parent = context.mock(ServiceRegistry.class);
-        TestRegistry registry = new TestRegistry(parent);
-
-        context.checking(new Expectations() {{
-            one(parent).get(Long.class);
-            will(returnValue(110L));
-        }});
-
-        assertThat(registry.get(Long.class), equalTo(120L));
-    }
-
-    @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"));
-        assertThat(registry.get(Integer.class), equalTo(12));
-    }
-    
-    @Test
-    public void closeInvokesCloseMethodOnEachService() {
-        final TestCloseService service = context.mock(TestCloseService.class);
-        registry.add(TestCloseService.class, service);
-
-        context.checking(new Expectations() {{
-            one(service).close();
-        }});
-
-        registry.close();
-    }
-
-    @Test
-    public void closeInvokesStopMethodOnEachService() {
-        final TestStopService service = context.mock(TestStopService.class);
-        registry.add(TestStopService.class, service);
-
-        context.checking(new Expectations() {{
-            one(service).stop();
-        }});
-
-        registry.close();
-    }
-
-    @Test
-    public void closeIgnoresServiceWithNoCloseOrStopMethod() {
-        registry.add(String.class, "service");
-
-        registry.close();
-    }
-
-    @Test
-    public void discardsServicesOnClose() {
-        registry.get(String.class);
-        registry.close();
-        try {
-            registry.get(String.class);
-            fail();
-        } catch (IllegalStateException e) {
-            assertThat(e.getMessage(), equalTo("Cannot locate service of type String, as TestRegistry has been closed."));
-        }
-    }
-
-    @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() {
-        }
-
-        public TestRegistry(ServiceRegistry parent) {
-            super(parent);
-        }
-
-        protected String createString() {
-            return get(Integer.class).toString();
-        }
-
-        protected Long createLong(Long value) {
-            return value + 10;
-        }
-
-        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();
-    }
-
-    public interface TestStopService {
-        void stop();
-    }
-}
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.internal.Factory;
+import org.jmock.Expectations;
+import org.jmock.integration.junit4.JMock;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.math.BigDecimal;
+import java.util.Map;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+ at RunWith(JMock.class)
+public class DefaultServiceRegistryTest {
+    private final JUnit4Mockery context = new JUnit4Mockery();
+    private final TestRegistry registry = new TestRegistry();
+
+    @Test
+    public void throwsExceptionForUnknownService() {
+        try {
+            registry.get(Map.class);
+            fail();
+        } catch (IllegalArgumentException e) {
+            assertThat(e.getMessage(), equalTo("No service of type Map available in TestRegistry."));
+        }
+    }
+
+    @Test
+    public void delegatesToParentForUnknownService() {
+        final BigDecimal value = BigDecimal.TEN;
+        final ServiceRegistry parent = context.mock(ServiceRegistry.class);
+        TestRegistry registry = new TestRegistry(parent);
+
+        context.checking(new Expectations(){{
+            one(parent).get(BigDecimal.class);
+            will(returnValue(value));
+        }});
+
+        assertThat(registry.get(BigDecimal.class), sameInstance(value));
+    }
+
+    @Test
+    public void throwsExceptionForUnknownParentService() {
+        final ServiceRegistry parent = context.mock(ServiceRegistry.class);
+        TestRegistry registry = new TestRegistry(parent);
+
+        context.checking(new Expectations(){{
+            one(parent).get(Map.class);
+            will(throwException(new DefaultServiceRegistry.UnknownServiceException(Map.class, "fail")));
+        }});
+
+        try {
+            registry.get(Map.class);
+            fail();
+        } catch (IllegalArgumentException e) {
+            assertThat(e.getMessage(), equalTo("No service of type Map available in TestRegistry."));
+        }
+    }
+
+    @Test
+    public void returnsAddedServiceInstance() {
+        BigDecimal value = BigDecimal.TEN;
+        registry.add(BigDecimal.class, value);
+        assertThat(registry.get(BigDecimal.class), sameInstance(value));
+        assertThat(registry.get(Number.class), sameInstance((Object) value));
+    }
+
+    @Test
+    public void createsAndCachesRegisteredServiceInstance() {
+        final BigDecimal value = BigDecimal.TEN;
+        registry.add(new DefaultServiceRegistry.Service(BigDecimal.class) {
+            @Override
+            protected Object create() {
+                return value;
+            }
+        });
+        assertThat(registry.get(BigDecimal.class), sameInstance(value));
+        assertThat(registry.get(Number.class), sameInstance((Object) value));
+    }
+
+    @Test
+    public void usesFactoryMethodToCreateServiceInstance() {
+        assertThat(registry.get(String.class), equalTo("12"));
+        assertThat(registry.get(Integer.class), equalTo(12));
+    }
+
+    @Test
+    public void usesDecoratorMethodToDecorateParentServiceInstance() {
+        final ServiceRegistry parent = context.mock(ServiceRegistry.class);
+        TestRegistry registry = new TestRegistry(parent);
+
+        context.checking(new Expectations() {{
+            one(parent).get(Long.class);
+            will(returnValue(110L));
+        }});
+
+        assertThat(registry.get(Long.class), equalTo(120L));
+    }
+
+    @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"));
+        assertThat(registry.get(Integer.class), equalTo(12));
+    }
+    
+    @Test
+    public void closeInvokesCloseMethodOnEachService() {
+        final TestCloseService service = context.mock(TestCloseService.class);
+        registry.add(TestCloseService.class, service);
+
+        context.checking(new Expectations() {{
+            one(service).close();
+        }});
+
+        registry.close();
+    }
+
+    @Test
+    public void closeInvokesStopMethodOnEachService() {
+        final TestStopService service = context.mock(TestStopService.class);
+        registry.add(TestStopService.class, service);
+
+        context.checking(new Expectations() {{
+            one(service).stop();
+        }});
+
+        registry.close();
+    }
+
+    @Test
+    public void closeIgnoresServiceWithNoCloseOrStopMethod() {
+        registry.add(String.class, "service");
+
+        registry.close();
+    }
+
+    @Test
+    public void discardsServicesOnClose() {
+        registry.get(String.class);
+        registry.close();
+        try {
+            registry.get(String.class);
+            fail();
+        } catch (IllegalStateException e) {
+            assertThat(e.getMessage(), equalTo("Cannot locate service of type String, as TestRegistry has been closed."));
+        }
+    }
+
+    @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() {
+        }
+
+        public TestRegistry(ServiceRegistry parent) {
+            super(parent);
+        }
+
+        protected String createString() {
+            return get(Integer.class).toString();
+        }
+
+        protected Long createLong(Long value) {
+            return value + 10;
+        }
+
+        protected Integer createInt() {
+            return 12;
+        }
+
+        protected Factory<BigDecimal> createTestFactory() {
+            return new TestFactory();
+        }
+
+        protected Factory<Long> createLongFactory(final Factory<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();
+    }
+
+    public interface TestStopService {
+        void stop();
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/ProjectFactoryTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/ProjectFactoryTest.java
index 387cbdf..51e0613 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/ProjectFactoryTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/ProjectFactoryTest.java
@@ -31,6 +31,7 @@ import org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandler;
 import org.gradle.groovy.scripts.ScriptSource;
 import org.gradle.groovy.scripts.StringScriptSource;
 import org.gradle.groovy.scripts.UriScriptSource;
+import org.gradle.testfixtures.internal.TestTopLevelBuildServiceRegistry;
 import org.gradle.util.MultiParentClassLoader;
 import org.gradle.util.TemporaryFolder;
 import org.jmock.Expectations;
@@ -66,7 +67,7 @@ public class ProjectFactoryTest {
     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);
+    private ServiceRegistryFactory serviceRegistryFactory = new TestTopLevelBuildServiceRegistry(new GlobalServicesRegistry(), startParameterStub, rootDir);
     private ClassGenerator classGeneratorMock = serviceRegistryFactory.get(ClassGenerator.class);
     private GradleInternal gradle = context.mock(GradleInternal.class);
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TopLevelBuildServiceRegistryTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TopLevelBuildServiceRegistryTest.java
index 3584517..113c36e 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TopLevelBuildServiceRegistryTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TopLevelBuildServiceRegistryTest.java
@@ -25,7 +25,7 @@ import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.artifacts.dsl.DefaultPublishArtifactFactory;
 import org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandlerFactory;
 import org.gradle.api.internal.artifacts.dsl.PublishArtifactFactory;
-import org.gradle.api.internal.tasks.ExecuteAtMostOnceTaskExecuter;
+import org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter;
 import org.gradle.api.internal.tasks.TaskExecuter;
 import org.gradle.cache.CacheFactory;
 import org.gradle.cache.CacheRepository;
@@ -145,6 +145,7 @@ public class TopLevelBuildServiceRegistryTest {
 
     @Test
     public void providesARepositoryHandlerFactory() {
+        allowGetCoreImplClassLoader();
         assertThat(factory.getFactory(RepositoryHandler.class), instanceOf(DefaultRepositoryHandlerFactory.class));
     }
 
@@ -163,6 +164,7 @@ public class TopLevelBuildServiceRegistryTest {
 
     @Test
     public void providesAnInitScriptHandler() {
+        allowGetCoreImplClassLoader();
         expectScriptClassLoaderCreated();
         expectListenerManagerCreated();
         assertThat(factory.get(InitScriptHandler.class), instanceOf(InitScriptHandler.class));
@@ -171,6 +173,7 @@ public class TopLevelBuildServiceRegistryTest {
 
     @Test
     public void providesAScriptObjectConfigurerFactory() {
+        allowGetCoreImplClassLoader();
         expectListenerManagerCreated();
         expectScriptClassLoaderCreated();
         assertThat(factory.get(ScriptPluginFactory.class), instanceOf(DefaultScriptPluginFactory.class));
@@ -179,6 +182,7 @@ public class TopLevelBuildServiceRegistryTest {
 
     @Test
     public void providesASettingsProcessor() {
+        allowGetCoreImplClassLoader();
         expectListenerManagerCreated();
         expectScriptClassLoaderCreated();
         assertThat(factory.get(SettingsProcessor.class), instanceOf(PropertiesLoadingSettingsProcessor.class));
@@ -194,12 +198,7 @@ public class TopLevelBuildServiceRegistryTest {
 
     @Test
     public void providesAWorkerProcessFactory() {
-        context.checking(new Expectations() {{
-            one(classLoaderFactory).getRootClassLoader();
-            will(returnValue(new ClassLoader() {
-            }));
-        }});
-
+        allowGetRootClassLoader();
         assertThat(factory.getFactory(WorkerProcessBuilder.class), instanceOf(DefaultWorkerProcessFactory.class));
     }
 
@@ -239,4 +238,20 @@ public class TopLevelBuildServiceRegistryTest {
             will(returnValue(new MultiParentClassLoader()));
         }});
     }
+
+    private void allowGetRootClassLoader() {
+        context.checking(new Expectations() {{
+            allowing(classLoaderFactory).getRootClassLoader();
+            will(returnValue(new ClassLoader() {
+            }));
+        }});
+    }
+
+    private void allowGetCoreImplClassLoader() {
+        context.checking(new Expectations() {{
+            allowing(classLoaderFactory).getCoreImplClassLoader();
+            will(returnValue(new ClassLoader() {
+            }));
+        }});
+    }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/AnnotationProcessingTaskFactoryTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/AnnotationProcessingTaskFactoryTest.java
index ec49817..f7bd574 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/AnnotationProcessingTaskFactoryTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/AnnotationProcessingTaskFactoryTest.java
@@ -16,7 +16,10 @@
 
 package org.gradle.api.internal.project.taskfactory;
 
-import org.gradle.api.*;
+import org.gradle.api.Action;
+import org.gradle.api.DefaultTask;
+import org.gradle.api.GradleException;
+import org.gradle.api.Task;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.internal.AbstractTask;
 import org.gradle.api.internal.TaskInternal;
@@ -36,12 +39,15 @@ import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.io.File;
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.concurrent.Callable;
 
-import static org.gradle.util.Matchers.*;
-import static org.gradle.util.WrapUtil.*;
+import static org.gradle.util.Matchers.isEmpty;
+import static org.gradle.util.WrapUtil.toList;
+import static org.gradle.util.WrapUtil.toSet;
 import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.*;
 
@@ -207,20 +213,19 @@ public class AnnotationProcessingTaskFactoryTest {
     @Test
     public void validationActionFailsWhenInputFileNotSpecified() {
         TaskWithInputFile task = expectTaskCreated(TaskWithInputFile.class, new Object[]{null});
-        assertValidationFails(task, "Error validating task ':task': No value has been specified for property 'inputFile'.");
+        assertValidationFails(task, "No value has been specified for property 'inputFile'.");
     }
 
     @Test
     public void validationActionFailsWhenInputFileDoesNotExist() {
         TaskWithInputFile task = expectTaskCreated(TaskWithInputFile.class, missingFile);
-        assertValidationFails(task, String.format("Error validating task ':task': File '%s' specified for property 'inputFile' does not exist.",
-                task.inputFile));
+        assertValidationFails(task, String.format("File '%s' specified for property 'inputFile' does not exist.", task.inputFile));
     }
 
     @Test
     public void validationActionFailsWhenInputFileIsADirectory() {
         TaskWithInputFile task = expectTaskCreated(TaskWithInputFile.class, existingDir);
-        assertValidationFails(task, String.format("Error validating task ':task': File '%s' specified for property 'inputFile' is not a file.",
+        assertValidationFails(task, String.format("File '%s' specified for property 'inputFile' is not a file.",
                 task.inputFile));
     }
 
@@ -243,7 +248,7 @@ public class AnnotationProcessingTaskFactoryTest {
     }
 
     @Test
-    public void validationActionSucceedsWhenSpecifiedOutputFileIsNotAFile() {
+    public void validationActionSucceedsWhenSpecifiedOutputFileDoesNotExist() {
         TaskWithOutputFile task = expectTaskCreated(TaskWithOutputFile.class, new File(testDir, "subdir/output.txt"));
 
         task.execute();
@@ -252,16 +257,22 @@ public class AnnotationProcessingTaskFactoryTest {
     }
 
     @Test
+    public void validationActionSucceedsWhenOptionalOutputFileNotSpecified() {
+        TaskWithOptionalOutputFile task = expectTaskCreated(TaskWithOptionalOutputFile.class);
+        task.execute();
+    }
+
+    @Test
     public void validationActionFailsWhenOutputFileNotSpecified() {
         TaskWithOutputFile task = expectTaskCreated(TaskWithOutputFile.class, new Object[]{null});
-        assertValidationFails(task, "Error validating task ':task': No value has been specified for property 'outputFile'.");
+        assertValidationFails(task, "No value has been specified for property 'outputFile'.");
     }
 
     @Test
     public void validationActionFailsWhenSpecifiedOutputFileIsADirectory() {
         TaskWithOutputFile task = expectTaskCreated(TaskWithOutputFile.class, existingDir);
         assertValidationFails(task, String.format(
-                "Error validating task ':task': Cannot write to file '%s' specified for property 'outputFile' as it is a directory.",
+                "Cannot write to file '%s' specified for property 'outputFile' as it is a directory.",
                 task.outputFile));
     }
 
@@ -270,9 +281,8 @@ public class AnnotationProcessingTaskFactoryTest {
         TaskWithOutputFile task = expectTaskCreated(TaskWithOutputFile.class, new File(testDir, "subdir/output.txt"));
         GFileUtils.touch(task.outputFile.getParentFile());
 
-        assertValidationFails(task, String.format(
-                "Error validating task ':task': Cannot create parent directory '%s' of file specified for property 'outputFile'.",
-                task.outputFile.getParentFile()));
+        assertValidationFails(task, String.format("Cannot write to file '%s' specified for property 'outputFile', as ancestor '%s' is not a directory.",
+                task.getOutputFile(), task.outputFile.getParentFile()));
     }
 
     @Test
@@ -296,7 +306,7 @@ public class AnnotationProcessingTaskFactoryTest {
     @Test
     public void validationActionFailsWhenInputFilesNotSpecified() {
         TaskWithInputFiles task = expectTaskCreated(TaskWithInputFiles.class, new Object[]{null});
-        assertValidationFails(task, "Error validating task ':task': No value has been specified for property 'input'.");
+        assertValidationFails(task, "No value has been specified for property 'input'.");
     }
 
     @Test
@@ -315,11 +325,11 @@ public class AnnotationProcessingTaskFactoryTest {
     public void skipsTaskWhenInputFileCollectionIsEmpty() {
         final FileCollection inputFiles = context.mock(FileCollection.class);
         context.checking(new Expectations() {{
-            one(inputFiles).stopExecutionIfEmpty();
-            will(throwException(new StopExecutionException()));
+            one(inputFiles).isEmpty();
+            will(returnValue(true));
         }});
 
-        TaskWithInputFiles task = expectTaskCreated(BrokenTaskWithInputFiles.class, inputFiles);
+        BrokenTaskWithInputFiles task = expectTaskCreated(BrokenTaskWithInputFiles.class, inputFiles);
 
         task.execute();
     }
@@ -339,15 +349,21 @@ public class AnnotationProcessingTaskFactoryTest {
     }
 
     @Test
+    public void validationActionSucceedsWhenOptionalOutputDirectoryNotSpecified() {
+        TaskWithOptionalOutputDir task = expectTaskCreated(TaskWithOptionalOutputDir.class);
+        task.execute();
+    }
+
+    @Test
     public void validationActionFailsWhenOutputDirectoryNotSpecified() {
         TaskWithOutputDir task = expectTaskCreated(TaskWithOutputDir.class, new Object[]{null});
-        assertValidationFails(task, "Error validating task ':task': No value has been specified for property 'outputDir'.");
+        assertValidationFails(task, "No value has been specified for property 'outputDir'.");
     }
 
     @Test
     public void validationActionFailsWhenOutputDirectoryIsAFile() {
         TaskWithOutputDir task = expectTaskCreated(TaskWithOutputDir.class, existingFile);
-        assertValidationFails(task, String.format("Error validating task ':task': Cannot create directory '%s' specified for property 'outputDir'.",
+        assertValidationFails(task, String.format("Directory '%s' specified for property 'outputDir' is not a directory.",
                 task.outputDir));
     }
 
@@ -356,8 +372,7 @@ public class AnnotationProcessingTaskFactoryTest {
         TaskWithOutputDir task = expectTaskCreated(TaskWithOutputDir.class, new File(testDir, "subdir/output"));
         GFileUtils.touch(task.outputDir.getParentFile());
 
-        assertValidationFails(task, String.format("Error validating task ':task': Cannot create directory '%s' specified for property 'outputDir'.",
-                task.outputDir));
+        assertValidationFails(task, String.format("Cannot write to directory '%s' specified for property 'outputDir', as ancestor '%s' is not a directory.", task.outputDir, task.outputDir.getParentFile()));
     }
 
     @Test
@@ -381,13 +396,13 @@ public class AnnotationProcessingTaskFactoryTest {
     @Test
     public void validationActionFailsWhenInputDirectoryNotSpecified() {
         TaskWithInputDir task = expectTaskCreated(TaskWithInputDir.class, new Object[]{null});
-        assertValidationFails(task, "Error validating task ':task': No value has been specified for property 'inputDir'.");
+        assertValidationFails(task, "No value has been specified for property 'inputDir'.");
     }
     
     @Test
     public void validationActionFailsWhenInputDirectoryDoesNotExist() {
         TaskWithInputDir task = expectTaskCreated(TaskWithInputDir.class, missingDir);
-        assertValidationFails(task, String.format("Error validating task ':task': Directory '%s' specified for property 'inputDir' does not exist.",
+        assertValidationFails(task, String.format("Directory '%s' specified for property 'inputDir' does not exist.",
                 task.inputDir));
     }
 
@@ -397,7 +412,7 @@ public class AnnotationProcessingTaskFactoryTest {
         GFileUtils.touch(task.inputDir);
 
         assertValidationFails(task, String.format(
-                "Error validating task ':task': Directory '%s' specified for property 'inputDir' is not a directory.", task.inputDir));
+                "Directory '%s' specified for property 'inputDir' is not a directory.", task.inputDir));
     }
 
     @Test
@@ -420,12 +435,6 @@ public class AnnotationProcessingTaskFactoryTest {
     }
 
     @Test
-    public void skipsTaskWhenInputDirectoryIsDoesNotExistAndSkipWhenEmpty() {
-        TaskWithInputDir task = expectTaskCreated(BrokenTaskWithInputDir.class, missingDir);
-        task.execute();
-    }
-
-    @Test
     public void validationActionSucceedsWhenInputValueSpecified() {
         TaskWithInput task = expectTaskCreated(TaskWithInput.class, "value");
         task.execute();
@@ -434,7 +443,7 @@ public class AnnotationProcessingTaskFactoryTest {
     @Test
     public void validationActionFailsWhenInputValueNotSpecified() {
         TaskWithInput task = expectTaskCreated(TaskWithInput.class, new Object[]{null});
-        assertValidationFails(task, "Error validating task ':task': No value has been specified for property 'inputValue'.");
+        assertValidationFails(task, "No value has been specified for property 'inputValue'.");
     }
 
     @Test
@@ -452,7 +461,7 @@ public class AnnotationProcessingTaskFactoryTest {
     @Test
     public void validatesNestedBeans() {
         TaskWithNestedBean task = expectTaskCreated(TaskWithNestedBean.class, new Object[]{null});
-        assertValidationFails(task, "Error validating task ':task': No value has been specified for property 'bean.inputFile'.");
+        assertValidationFails(task, "No value has been specified for property 'bean.inputFile'.");
     }
 
     @Test
@@ -471,7 +480,7 @@ public class AnnotationProcessingTaskFactoryTest {
     public void validationFailsWhenNestedBeanIsNull() {
         TaskWithNestedBean task = expectTaskCreated(TaskWithNestedBean.class, new Object[]{null});
         task.bean = null;
-        assertValidationFails(task, "Error validating task ':task': No value has been specified for property 'bean'.");
+        assertValidationFails(task, "No value has been specified for property 'bean'.");
     }
 
     @Test
@@ -483,16 +492,32 @@ public class AnnotationProcessingTaskFactoryTest {
     @Test
     public void canAttachAnnotationToGroovyProperty() {
         InputFileTask task = expectTaskCreated(InputFileTask.class);
-        assertValidationFails(task, "Error validating task ':task': No value has been specified for property 'srcFile'.");
+        assertValidationFails(task, "No value has been specified for property 'srcFile'.");
     }
-    
-    private void assertValidationFails(TaskInternal task, String expectedErrorMessage) {
+
+    @Test
+    public void validationFailureListsViolationsForAllProperties() {
+        TaskWithMultipleProperties task = expectTaskCreated(TaskWithMultipleProperties.class, new Object[]{null});
+        assertValidationFails(task,
+                "No value has been specified for property 'outputFile'.",
+                "No value has been specified for property 'bean.inputFile'.");
+    }
+
+    private void assertValidationFails(TaskInternal task, String... expectedErrorMessages) {
         try {
             task.execute();
             fail();
-        } catch (GradleException e) {
-            assertThat(e.getCause(), instanceOf(InvalidUserDataException.class));
-            assertThat(e.getCause().getMessage(), equalTo(expectedErrorMessage));
+        } catch (TaskValidationException e) {
+            if (expectedErrorMessages.length == 1) {
+                assertThat(e.getMessage(), containsString("A problem was found with the configuration of " + task));
+            } else {
+                assertThat(e.getMessage(), containsString("Some problems were found with the configuration of " + task));
+            }
+            HashSet<String> actualMessages = new HashSet<String>();
+            for (Throwable cause : e.getCauses()) {
+                actualMessages.add(cause.getMessage());
+            }
+            assertThat(actualMessages, equalTo(new HashSet<String>(Arrays.asList(expectedErrorMessages))));
         }
     }
 
@@ -639,6 +664,13 @@ public class AnnotationProcessingTaskFactoryTest {
         }
     }
 
+    public static class TaskWithOptionalOutputFile extends DefaultTask {
+        @OutputFile @Optional
+        public File getOutputFile() {
+            return null;
+        }
+    }
+
     public static class TaskWithOutputDir extends DefaultTask {
         File outputDir;
 
@@ -652,6 +684,13 @@ public class AnnotationProcessingTaskFactoryTest {
         }
     }
 
+    public static class TaskWithOptionalOutputDir extends DefaultTask {
+        @OutputDirectory @Optional
+        public File getOutputDir() {
+            return null;
+        }
+    }
+
     public static class TaskWithInputFiles extends DefaultTask {
         Iterable<? extends File> input;
 
@@ -659,7 +698,7 @@ public class AnnotationProcessingTaskFactoryTest {
             this.input = input;
         }
 
-        @InputFiles @SkipWhenEmpty
+        @InputFiles
         public Iterable<? extends File> getInput() {
             return input;
         }
@@ -670,6 +709,11 @@ public class AnnotationProcessingTaskFactoryTest {
             super(input);
         }
 
+        @InputFiles @SkipWhenEmpty
+        public Iterable<? extends File> getInput() {
+            return input;
+        }
+
         @TaskAction
         public void doStuff() {
             fail();
@@ -696,6 +740,17 @@ public class AnnotationProcessingTaskFactoryTest {
         }
     }
 
+    public static class TaskWithMultipleProperties extends TaskWithNestedBean {
+        public TaskWithMultipleProperties(File inputFile) {
+            super(inputFile);
+        }
+
+        @OutputFile
+        public File getOutputFile() {
+            return bean.getInputFile();
+        }
+    }
+
     public static class TaskWithOptionalNestedBean extends DefaultTask {
         @Nested @Optional
         public Bean getBean() {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/ExecutionShortCircuitTaskExecuterTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/ExecutionShortCircuitTaskExecuterTest.java
deleted file mode 100644
index 641b348..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/ExecutionShortCircuitTaskExecuterTest.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.internal.project.taskfactory;
-
-import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.internal.TaskOutputsInternal;
-import org.gradle.api.internal.changedetection.TaskArtifactState;
-import org.gradle.api.internal.changedetection.TaskArtifactStateRepository;
-import org.gradle.api.internal.tasks.TaskExecuter;
-import org.gradle.api.internal.tasks.TaskStateInternal;
-import org.gradle.util.JUnit4GroovyMockery;
-import org.jmock.Expectations;
-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.runner.RunWith;
-
- at RunWith(JMock.class)
-public class ExecutionShortCircuitTaskExecuterTest {
-    private final JUnit4Mockery context = new JUnit4GroovyMockery();
-    private final TaskExecuter delegate = context.mock(TaskExecuter.class);
-    private final TaskOutputsInternal outputs = context.mock(TaskOutputsInternal.class);
-    private final TaskInternal task = context.mock(TaskInternal.class);
-    private final TaskStateInternal taskState = context.mock(TaskStateInternal.class);
-    private final TaskArtifactStateRepository repository = context.mock(TaskArtifactStateRepository.class);
-    private final TaskArtifactState taskArtifactState = context.mock(TaskArtifactState.class);
-    private final ExecutionShortCircuitTaskExecuter executer = new ExecutionShortCircuitTaskExecuter(delegate, repository);
-
-    @Before
-    public void setup() {
-
-        context.checking(new Expectations(){{
-            allowing(task).getOutputs();
-            will(returnValue(outputs));
-        }});
-    }
-    @Test
-    public void skipsTaskWhenOutputsAreUpToDate() {
-        context.checking(new Expectations() {{
-            one(repository).getStateFor(task);
-            will(returnValue(taskArtifactState));
-
-            one(taskArtifactState).isUpToDate();
-            will(returnValue(true));
-
-            one(taskState).upToDate();
-        }});
-
-        executer.execute(task, taskState);
-    }
-    
-    @Test
-    public void executesTaskWhenOutputsAreNotUpToDate() {
-        context.checking(new Expectations() {{
-            Sequence sequence = context.sequence("seq");
-
-            one(repository).getStateFor(task);
-            will(returnValue(taskArtifactState));
-            inSequence(sequence);
-
-            one(taskArtifactState).isUpToDate();
-            will(returnValue(false));
-            inSequence(sequence);
-
-            one(outputs).setHistory(taskArtifactState);
-            inSequence(sequence);
-
-            one(delegate).execute(task, taskState);
-            inSequence(sequence);
-
-            allowing(taskState).getFailure();
-            will(returnValue(null));
-
-            one(taskArtifactState).update();
-            inSequence(sequence);
-
-            one(outputs).setHistory(null);
-            inSequence(sequence);
-        }});
-
-        executer.execute(task, taskState);
-    }
-
-    @Test
-    public void doesNotUpdateStateWhenTaskFails() {
-        context.checking(new Expectations() {{
-            one(repository).getStateFor(task);
-            will(returnValue(taskArtifactState));
-
-            one(taskArtifactState).isUpToDate();
-            will(returnValue(false));
-
-            one(outputs).setHistory(taskArtifactState);
-
-            one(delegate).execute(task, taskState);
-
-            allowing(taskState).getFailure();
-            will(returnValue(new RuntimeException()));
-
-            one(outputs).setHistory(null);
-        }});
-
-        executer.execute(task, taskState);
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/PostExecutionAnalysisTaskExecuterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/PostExecutionAnalysisTaskExecuterTest.groovy
deleted file mode 100644
index b27bcee..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/PostExecutionAnalysisTaskExecuterTest.groovy
+++ /dev/null
@@ -1,111 +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.project.taskfactory
-
-import org.gradle.api.Action
-import org.gradle.api.internal.TaskInternal
-import org.gradle.api.internal.tasks.TaskExecuter
-import org.gradle.api.internal.tasks.TaskStateInternal
-import org.gradle.api.tasks.TaskDependency
-import org.gradle.util.JUnit4GroovyMockery
-import org.jmock.integration.junit4.JMock
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
- at RunWith(JMock.class)
-class PostExecutionAnalysisTaskExecuterTest {
-    private final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
-    private final TaskExecuter target = context.mock(TaskExecuter.class)
-    private final TaskInternal task = context.mock(TaskInternal.class)
-    private final TaskInternal dependency = context.mock(TaskInternal.class)
-    private final TaskDependency taskDependency = context.mock(TaskDependency.class)
-    private final TaskStateInternal state = context.mock(TaskStateInternal.class)
-    private final TaskStateInternal dependencyState = context.mock(TaskStateInternal.class)
-    private final PostExecutionAnalysisTaskExecuter executer = new PostExecutionAnalysisTaskExecuter(target)
-
-    @Before
-    public void setup() {
-        context.checking {
-            allowing(task).getTaskDependencies()
-            will(returnValue(taskDependency))
-            allowing(dependency).getState()
-            will(returnValue(dependencyState))
-        }
-    }
-    
-    @Test
-    public void marksTaskUpToDateWhenItHasNoActionsAndAllOfItsDependenciesWereSkipped() {
-        context.checking {
-            one(target).execute(task, state)
-            allowing(task).getActions();
-            will(returnValue([]))
-            allowing(taskDependency).getDependencies(task)
-            will(returnValue([dependency] as Set))
-            allowing(dependencyState).getSkipped()
-            will(returnValue(true))
-            one(state).upToDate()
-        }
-
-        executer.execute(task, state)
-    }
-
-    @Test
-    public void doesNotMarkTaskUpToDateWhenAnyDependencyWasNotSkipped() {
-        context.checking {
-            one(target).execute(task, state)
-            allowing(task).getActions();
-            will(returnValue([]))
-            allowing(taskDependency).getDependencies(task)
-            will(returnValue([dependency] as Set))
-            allowing(dependencyState).getSkipped()
-            will(returnValue(false))
-        }
-
-        executer.execute(task, state)
-    }
-
-    @Test
-    public void marksTaskUpToDateWhenItHasActionsAndItDidNotDoWork() {
-        context.checking {
-            one(target).execute(task, state)
-            allowing(task).getActions();
-            will(returnValue([{} as Action]))
-            allowing(state).getDidWork()
-            will(returnValue(false))
-            one(state).upToDate()
-        }
-
-        executer.execute(task, state)
-    }
-
-    @Test
-    public void doesNotMarkTaskUpToDateWhenItHasActionsAndDidWork() {
-        context.checking {
-            one(target).execute(task, state)
-            allowing(task).getActions();
-            will(returnValue([{} as Action]))
-            allowing(state).getDidWork()
-            will(returnValue(true))
-        }
-
-        executer.execute(task, state)
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/TaskFactoryTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/TaskFactoryTest.java
index 7ccc7fe..500be51 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/TaskFactoryTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/TaskFactoryTest.java
@@ -188,6 +188,13 @@ public class TaskFactoryTest {
         assertEquals("9", task.getDescription());
     }
 
+    @Test
+    public void createTaskWithGroup() {
+        Object testGroup = "The Group";
+        Task task = checkTask(taskFactory.createTask(testProject, GUtil.map(Task.TASK_NAME, "task", Task.TASK_GROUP, testGroup)));
+        assertEquals(testGroup, task.getGroup());
+    }
+
     private Task checkTask(Task task) {
         assertEquals(TEST_TASK_NAME, task.getName());
         assertSame(testProject, task.getProject());
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskExecuterTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskExecuterTest.java
deleted file mode 100644
index ece3aee..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskExecuterTest.java
+++ /dev/null
@@ -1,281 +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;
-
-import org.gradle.api.Action;
-import org.gradle.api.Task;
-import org.gradle.api.execution.TaskActionListener;
-import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.logging.StandardOutputCapture;
-import org.gradle.api.tasks.StopActionException;
-import org.gradle.api.tasks.StopExecutionException;
-import org.gradle.api.tasks.TaskExecutionException;
-import org.gradle.groovy.scripts.ScriptSource;
-import org.gradle.util.JUnit4GroovyMockery;
-import org.jmock.Expectations;
-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.runner.RunWith;
-
-import static org.gradle.util.Matchers.*;
-import static org.gradle.util.WrapUtil.*;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
- at RunWith(JMock.class)
-public class DefaultTaskExecuterTest {
-    private final JUnit4Mockery context = new JUnit4GroovyMockery();
-    private final TaskInternal task = context.mock(TaskInternal.class, "<task>");
-    private final Action<Task> action1 = context.mock(Action.class, "action1");
-    private final Action<Task> action2 = context.mock(Action.class, "action2");
-    private final TaskStateInternal state = context.mock(TaskStateInternal.class);
-    private final ScriptSource scriptSource = context.mock(ScriptSource.class);
-    private final StandardOutputCapture standardOutputCapture = context.mock(StandardOutputCapture.class);
-    private final Sequence sequence = context.sequence("seq");
-    private final TaskActionListener listener = context.mock(TaskActionListener.class);
-    private final DefaultTaskExecuter executer = new DefaultTaskExecuter(listener);
-
-    @Before
-    public void setUp() {
-        context.checking(new Expectations(){{
-            ProjectInternal project = context.mock(ProjectInternal.class);
-
-            allowing(task).getProject();
-            will(returnValue(project));
-
-            allowing(project).getBuildScriptSource();
-            will(returnValue(scriptSource));
-
-            allowing(task).getStandardOutputCapture();
-            will(returnValue(standardOutputCapture));
-
-            ignoring(scriptSource);
-        }});
-    }
-
-    @Test
-    public void doesNothingWhenTaskHasNoActions() {
-        context.checking(new Expectations() {{
-            allowing(task).getActions();
-            will(returnValue(toList()));
-
-            one(listener).beforeActions(task);
-            inSequence(sequence);
-
-            one(state).setExecuting(true);
-            inSequence(sequence);
-
-            one(state).executed(null);
-            inSequence(sequence);
-
-            one(state).setExecuting(false);
-            inSequence(sequence);
-
-            one(listener).afterActions(task);
-            inSequence(sequence);
-        }});
-
-        executer.execute(task, state);
-    }
-
-    @Test
-    public void executesEachActionInOrder() {
-        context.checking(new Expectations() {{
-            allowing(task).getActions();
-            will(returnValue(toList(action1, action2)));
-
-            one(listener).beforeActions(task);
-            inSequence(sequence);
-
-            one(state).setExecuting(true);
-            inSequence(sequence);
-
-            one(state).setDidWork(true);
-            inSequence(sequence);
-
-            one(standardOutputCapture).start();
-            inSequence(sequence);
-
-            one(action1).execute(task);
-            inSequence(sequence);
-
-            one(standardOutputCapture).stop();
-            inSequence(sequence);
-
-            one(state).setDidWork(true);
-            inSequence(sequence);
-
-            one(standardOutputCapture).start();
-            inSequence(sequence);
-
-            one(action2).execute(task);
-            inSequence(sequence);
-
-            one(standardOutputCapture).stop();
-            inSequence(sequence);
-
-            one(state).executed(null);
-            inSequence(sequence);
-            
-            one(state).setExecuting(false);
-            inSequence(sequence);
-
-            one(listener).afterActions(task);
-            inSequence(sequence);
-        }});
-
-        executer.execute(task, state);
-    }
-
-    @Test
-    public void stopsAtFirstActionWhichThrowsException() {
-        final Throwable failure = new RuntimeException("failure");
-        final Collector<Throwable> wrappedFailure = collector();
-        context.checking(new Expectations() {{
-            allowing(task).getActions();
-            will(returnValue(toList(action1, action2)));
-
-            one(listener).beforeActions(task);
-            inSequence(sequence);
-
-            one(state).setExecuting(true);
-            inSequence(sequence);
-
-            one(state).setDidWork(true);
-            inSequence(sequence);
-
-            one(standardOutputCapture).start();
-            inSequence(sequence);
-
-            one(action1).execute(task);
-            will(throwException(failure));
-            inSequence(sequence);
-
-            one(standardOutputCapture).stop();
-            inSequence(sequence);
-
-            one(state).executed(with(notNullValue(Throwable.class)));
-            will(collectTo(wrappedFailure));
-            inSequence(sequence);
-
-            one(state).setExecuting(false);
-            inSequence(sequence);
-
-            one(listener).afterActions(task);
-            inSequence(sequence);
-        }});
-
-        executer.execute(task, state);
-
-        assertThat(wrappedFailure.get(), instanceOf(TaskExecutionException.class));
-        TaskExecutionException exception = (TaskExecutionException) wrappedFailure.get();
-        assertThat(exception.getTask(), equalTo((Task) task));
-        assertThat(exception.getMessage(), equalTo("Execution failed for <task>."));
-        assertThat(exception.getCause(), sameInstance(failure));
-    }
-
-    @Test
-    public void stopsAtFirstActionWhichThrowsStopExecutionException() {
-        context.checking(new Expectations() {{
-            allowing(task).getActions();
-            will(returnValue(toList(action1, action2)));
-
-            one(listener).beforeActions(task);
-            inSequence(sequence);
-
-            one(state).setExecuting(true);
-            inSequence(sequence);
-
-            one(state).setDidWork(true);
-            inSequence(sequence);
-
-            one(standardOutputCapture).start();
-            inSequence(sequence);
-
-            one(action1).execute(task);
-            will(throwException(new StopExecutionException("stop")));
-            inSequence(sequence);
-
-            one(standardOutputCapture).stop();
-            inSequence(sequence);
-
-            one(state).executed(null);
-            inSequence(sequence);
-
-            one(state).setExecuting(false);
-            inSequence(sequence);
-
-            one(listener).afterActions(task);
-            inSequence(sequence);
-        }});
-
-        executer.execute(task, state);
-    }
-
-    @Test
-    public void skipsActionWhichThrowsStopActionException() {
-        context.checking(new Expectations() {{
-            allowing(task).getActions();
-            will(returnValue(toList(action1, action2)));
-
-            one(listener).beforeActions(task);
-            inSequence(sequence);
-
-            one(state).setExecuting(true);
-            inSequence(sequence);
-
-            one(state).setDidWork(true);
-            inSequence(sequence);
-
-            one(standardOutputCapture).start();
-            inSequence(sequence);
-
-            one(action1).execute(task);
-            will(throwException(new StopActionException("stop")));
-            inSequence(sequence);
-
-            one(standardOutputCapture).stop();
-            inSequence(sequence);
-
-            one(state).setDidWork(true);
-            inSequence(sequence);
-
-            one(standardOutputCapture).start();
-            inSequence(sequence);
-
-            one(action2).execute(task);
-            inSequence(sequence);
-
-            one(standardOutputCapture).stop();
-            inSequence(sequence);
-
-            one(state).executed(null);
-            inSequence(sequence);
-
-            one(state).setExecuting(false);
-            inSequence(sequence);
-
-            one(listener).afterActions(task);
-            inSequence(sequence);
-        }});
-
-        executer.execute(task, state);
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskInputsTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskInputsTest.groovy
index 801ddd8..2f4280a 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskInputsTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskInputsTest.groovy
@@ -15,91 +15,168 @@
  */
 package org.gradle.api.internal.tasks
 
-import org.junit.Test
-import static org.junit.Assert.*
-import static org.hamcrest.Matchers.*
-import static org.gradle.util.Matchers.*
-import org.gradle.api.internal.file.FileResolver
-import org.gradle.api.file.FileTree
 import java.util.concurrent.Callable
 import org.gradle.api.file.FileCollection
+import org.gradle.api.file.FileTree
+import org.gradle.api.internal.TaskInternal
+import org.gradle.api.internal.file.FileResolver
+import spock.lang.Specification
 
-class DefaultTaskInputsTest {
+class DefaultTaskInputsTest extends Specification {
     private final File treeFile = new File('tree')
     private final FileTree tree = [getFiles: { [treeFile] as Set}] as FileTree
     private final FileResolver resolver = [
             resolve: {new File(it)},
             resolveFilesAsTree: {tree}
     ] as FileResolver
-    private final DefaultTaskInputs inputs = new DefaultTaskInputs(resolver)
+    private final DefaultTaskInputs inputs = new DefaultTaskInputs(resolver, {} as TaskInternal)
 
-    @Test
-    public void defaultValues() {
-        assertThat(inputs.files.files, isEmpty())
-        assertFalse(inputs.hasInputs)
+    def defaultValues() {
+        expect:
+        inputs.files.empty
+        inputs.properties.isEmpty()
+        !inputs.hasInputs
+        !inputs.hasSourceFiles
+        inputs.sourceFiles.empty
     }
 
-    @Test
-    public void canRegisterInputFiles() {
+    def canRegisterInputFiles() {
+        when:
         inputs.files('a')
-        assertThat(inputs.files.files, equalTo([new File('a')] as Set))
+
+        then:
+        inputs.files.files == [new File('a')] as Set
     }
 
-    @Test
-    public void canRegisterInputDir() {
+    def canRegisterInputDir() {
+        when:
         inputs.dir('a')
-        assertThat(inputs.files.files, equalTo([treeFile] as Set))
+
+        then:
+        inputs.files.files == [treeFile] as Set
     }
-    
-    @Test
-    public void canRegisterInputProperty() {
+
+    def canRegisterInputProperty() {
+        when:
         inputs.property('a', 'value')
-        assertThat(inputs.properties, equalTo([a: 'value']))
+
+        then:
+        inputs.properties == [a: 'value']
     }
-    
-    @Test
-    public void canRegisterInputPropertyUsingAClosure() {
+
+    def canRegisterInputPropertyUsingAClosure() {
+        when:
         inputs.property('a', { 'value' })
-        assertThat(inputs.properties, equalTo([a: 'value']))
+
+        then:
+        inputs.properties == [a: 'value']
     }
 
-    @Test
-    public void canRegisterInputPropertyUsingACallable() {
+    def canRegisterInputPropertyUsingACallable() {
+        when:
         inputs.property('a', { 'value' } as Callable)
-        assertThat(inputs.properties, equalTo([a: 'value']))
+
+        then:
+        inputs.properties == [a: 'value']
     }
 
-    @Test
-    public void canRegisterInputPropertyUsingAFileCollection() {
+    def canRegisterInputPropertyUsingAFileCollection() {
         def files = [new File('file')] as Set
+
+        when:
         inputs.property('a', [getFiles: { files }] as FileCollection)
-        assertThat(inputs.properties, equalTo([a: files]))
+
+        then:
+        inputs.properties == [a: files]
     }
 
-    @Test
-    public void inputPropertyCanBeNestedCallableAndClosure() {
+    def inputPropertyCanBeNestedCallableAndClosure() {
         def files = [new File('file')] as Set
         def fileCollection = [getFiles: { files }] as FileCollection
         def callable = {fileCollection} as Callable
+
+        when:
         inputs.property('a', { callable })
-        assertThat(inputs.properties, equalTo([a: files]))
+
+        then:
+        inputs.properties == [a: files]
+    }
+
+    def canRegisterSourceFile() {
+        when:
+        inputs.source('file')
+
+        then:
+        inputs.sourceFiles.files == ([new File('file')] as Set)
+    }
+
+    def canRegisterSourceFiles() {
+        when:
+        inputs.source('file', 'file2')
+
+        then:
+        inputs.sourceFiles.files == ([new File('file'), new File('file2')] as Set)
+    }
+
+    def canRegisterSourceDir() {
+        when:
+        inputs.sourceDir('dir')
+
+        then:
+        inputs.sourceFiles.files == [treeFile] as Set
+    }
+
+    def sourceFilesAreAlsoInputFiles() {
+        when:
+        inputs.source('file')
+
+        then:
+        inputs.sourceFiles.files == ([new File('file')] as Set)
+        inputs.files.files == ([new File('file')] as Set)
     }
 
-    @Test
-    public void hasInputsWhenEmptyInputFilesRegistered() {
+    def hasInputsWhenEmptyInputFilesRegistered() {
+        when:
         inputs.files([])
-        assertTrue(inputs.hasInputs)
+
+        then:
+        inputs.hasInputs
+        !inputs.hasSourceFiles
     }
 
-    @Test
-    public void hasInputsWhenNonEmptyInputFilesRegistered() {
+    def hasInputsWhenNonEmptyInputFilesRegistered() {
+        when:
         inputs.files('a')
-        assertTrue(inputs.hasInputs)
+
+        then:
+        inputs.hasInputs
+        !inputs.hasSourceFiles
     }
 
-    @Test
-    public void hasInputsWhenInputPropertyRegistered() {
+    def hasInputsWhenInputPropertyRegistered() {
+        when:
         inputs.property('a', 'value')
-        assertTrue(inputs.hasInputs)
+
+        then:
+        inputs.hasInputs
+        !inputs.hasSourceFiles
+    }
+
+    def hasInputsWhenEmptySourceFilesRegistered() {
+        when:
+        inputs.source([])
+
+        then:
+        inputs.hasInputs
+        inputs.hasSourceFiles
+    }
+    
+    def hasInputsWhenSourceFilesRegistered() {
+        when:
+        inputs.source('a')
+
+        then:
+        inputs.hasInputs
+        inputs.hasSourceFiles
     }
 }
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/ExecuteAtMostOnceTaskExecuterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/ExecuteAtMostOnceTaskExecuterTest.groovy
deleted file mode 100644
index ed22980..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/ExecuteAtMostOnceTaskExecuterTest.groovy
+++ /dev/null
@@ -1,52 +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
-
-import org.gradle.api.internal.TaskInternal
-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 ExecuteAtMostOnceTaskExecuterTest {
-    private final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
-    private final TaskExecuter target = context.mock(TaskExecuter.class)
-    private final TaskInternal task = context.mock(TaskInternal.class)
-    private final TaskStateInternal state = context.mock(TaskStateInternal.class)
-    private final ExecuteAtMostOnceTaskExecuter executer = new ExecuteAtMostOnceTaskExecuter(target)
-
-    @Test
-    public void doesNothingWhenTaskHasAlreadyBeenExecuted() {
-        context.checking {
-            allowing(state).getExecuted()
-            will(returnValue(true))
-        }
-
-        executer.execute(task, state)
-    }
-
-    @Test
-    public void delegatesToExecuterWhenTaskHasNotBeenExecuted() {
-        context.checking {
-            allowing(state).getExecuted()
-            will(returnValue(false))
-            one(target).execute(task, state)
-        }
-
-        executer.execute(task, state)
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/SkipTaskExecuterTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/SkipTaskExecuterTest.java
deleted file mode 100644
index b9ae075..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/SkipTaskExecuterTest.java
+++ /dev/null
@@ -1,116 +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;
-
-import org.gradle.api.GradleException;
-import org.gradle.api.Task;
-import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.specs.Spec;
-import org.gradle.groovy.scripts.ScriptSource;
-import org.gradle.util.JUnit4GroovyMockery;
-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 static org.gradle.util.Matchers.*;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
- at RunWith(JMock.class)
-public class SkipTaskExecuterTest {
-    private final JUnit4Mockery context = new JUnit4GroovyMockery();
-    private final TaskInternal task = context.mock(TaskInternal.class, "<task>");
-    private final Spec<Task> spec = context.mock(Spec.class);
-    private final TaskStateInternal state = context.mock(TaskStateInternal.class);
-    private final ScriptSource scriptSource = context.mock(ScriptSource.class);
-    private final TaskExecuter delegate = context.mock(TaskExecuter.class);
-    private final SkipTaskExecuter executer = new SkipTaskExecuter(delegate);
-
-    @Before
-    public void setUp() {
-        context.checking(new Expectations(){{
-            ProjectInternal project = context.mock(ProjectInternal.class);
-
-            allowing(task).getProject();
-            will(returnValue(project));
-
-            allowing(project).getBuildScriptSource();
-            will(returnValue(scriptSource));
-
-            allowing(task).getOnlyIf();
-            will(returnValue(spec));
-
-            ignoring(scriptSource);
-        }});
-    }
-
-    @Test
-    public void executesTask() {
-        context.checking(new Expectations() {{
-            allowing(task).getEnabled();
-            will(returnValue(true));
-
-            allowing(spec).isSatisfiedBy(task);
-            will(returnValue(true));
-
-            one(delegate).execute(task, state);
-
-            one(state).executed();
-        }});
-
-        executer.execute(task, state);
-    }
-
-    @Test
-    public void skipsTaskWhoseOnlyIfPredicateIsFalse() {
-        context.checking(new Expectations() {{
-            allowing(task).getEnabled();
-            will(returnValue(true));
-            one(spec).isSatisfiedBy(task);
-            will(returnValue(false));
-            one(state).skipped("SKIPPED");
-            one(state).executed();
-        }});
-
-        executer.execute(task, state);
-    }
-
-    @Test
-    public void wrapsOnlyIfPredicateFailure() {
-        final Throwable failure = new RuntimeException();
-        final Collector<Throwable> wrappedFailure = collector();
-        context.checking(new Expectations() {{
-            allowing(task).getEnabled();
-            will(returnValue(true));
-            one(spec).isSatisfiedBy(task);
-            will(throwException(failure));
-            one(state).executed(with(notNullValue(GradleException.class)));
-            will(collectTo(wrappedFailure));
-            one(state).executed();
-        }});
-
-        executer.execute(task, state);
-
-        GradleException exception = (GradleException) wrappedFailure.get();
-        assertThat(exception.getMessage(), equalTo("Could not evaluate onlyIf predicate for <task>."));
-        assertThat(exception.getCause(), sameInstance(failure));
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/ExecuteActionsTaskExecuterTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/ExecuteActionsTaskExecuterTest.java
new file mode 100644
index 0000000..f960fd0
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/ExecuteActionsTaskExecuterTest.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.execution;
+
+import org.gradle.api.Action;
+import org.gradle.api.Task;
+import org.gradle.api.execution.TaskActionListener;
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.tasks.TaskStateInternal;
+import org.gradle.logging.StandardOutputCapture;
+import org.gradle.api.tasks.StopActionException;
+import org.gradle.api.tasks.StopExecutionException;
+import org.gradle.api.tasks.TaskExecutionException;
+import org.gradle.groovy.scripts.ScriptSource;
+import org.gradle.util.JUnit4GroovyMockery;
+import org.jmock.Expectations;
+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.runner.RunWith;
+
+import static org.gradle.util.Matchers.*;
+import static org.gradle.util.WrapUtil.*;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+ at RunWith(JMock.class)
+public class ExecuteActionsTaskExecuterTest {
+    private final JUnit4Mockery context = new JUnit4GroovyMockery();
+    private final TaskInternal task = context.mock(TaskInternal.class, "<task>");
+    private final Action<Task> action1 = context.mock(Action.class, "action1");
+    private final Action<Task> action2 = context.mock(Action.class, "action2");
+    private final TaskStateInternal state = context.mock(TaskStateInternal.class);
+    private final ScriptSource scriptSource = context.mock(ScriptSource.class);
+    private final StandardOutputCapture standardOutputCapture = context.mock(StandardOutputCapture.class);
+    private final Sequence sequence = context.sequence("seq");
+    private final TaskActionListener listener = context.mock(TaskActionListener.class);
+    private final ExecuteActionsTaskExecuter executer = new ExecuteActionsTaskExecuter(listener);
+
+    @Before
+    public void setUp() {
+        context.checking(new Expectations(){{
+            ProjectInternal project = context.mock(ProjectInternal.class);
+
+            allowing(task).getProject();
+            will(returnValue(project));
+
+            allowing(project).getBuildScriptSource();
+            will(returnValue(scriptSource));
+
+            allowing(task).getStandardOutputCapture();
+            will(returnValue(standardOutputCapture));
+
+            ignoring(scriptSource);
+        }});
+    }
+
+    @Test
+    public void doesNothingWhenTaskHasNoActions() {
+        context.checking(new Expectations() {{
+            allowing(task).getActions();
+            will(returnValue(toList()));
+
+            one(listener).beforeActions(task);
+            inSequence(sequence);
+
+            one(state).setExecuting(true);
+            inSequence(sequence);
+
+            one(state).executed(null);
+            inSequence(sequence);
+
+            one(state).setExecuting(false);
+            inSequence(sequence);
+
+            one(listener).afterActions(task);
+            inSequence(sequence);
+        }});
+
+        executer.execute(task, state);
+    }
+
+    @Test
+    public void executesEachActionInOrder() {
+        context.checking(new Expectations() {{
+            allowing(task).getActions();
+            will(returnValue(toList(action1, action2)));
+
+            one(listener).beforeActions(task);
+            inSequence(sequence);
+
+            one(state).setExecuting(true);
+            inSequence(sequence);
+
+            one(state).setDidWork(true);
+            inSequence(sequence);
+
+            one(standardOutputCapture).start();
+            inSequence(sequence);
+
+            one(action1).execute(task);
+            inSequence(sequence);
+
+            one(standardOutputCapture).stop();
+            inSequence(sequence);
+
+            one(state).setDidWork(true);
+            inSequence(sequence);
+
+            one(standardOutputCapture).start();
+            inSequence(sequence);
+
+            one(action2).execute(task);
+            inSequence(sequence);
+
+            one(standardOutputCapture).stop();
+            inSequence(sequence);
+
+            one(state).executed(null);
+            inSequence(sequence);
+            
+            one(state).setExecuting(false);
+            inSequence(sequence);
+
+            one(listener).afterActions(task);
+            inSequence(sequence);
+        }});
+
+        executer.execute(task, state);
+    }
+
+    @Test
+    public void stopsAtFirstActionWhichThrowsException() {
+        final Throwable failure = new RuntimeException("failure");
+        final Collector<Throwable> wrappedFailure = collector();
+        context.checking(new Expectations() {{
+            allowing(task).getActions();
+            will(returnValue(toList(action1, action2)));
+
+            one(listener).beforeActions(task);
+            inSequence(sequence);
+
+            one(state).setExecuting(true);
+            inSequence(sequence);
+
+            one(state).setDidWork(true);
+            inSequence(sequence);
+
+            one(standardOutputCapture).start();
+            inSequence(sequence);
+
+            one(action1).execute(task);
+            will(throwException(failure));
+            inSequence(sequence);
+
+            one(standardOutputCapture).stop();
+            inSequence(sequence);
+
+            one(state).executed(with(notNullValue(Throwable.class)));
+            will(collectTo(wrappedFailure));
+            inSequence(sequence);
+
+            one(state).setExecuting(false);
+            inSequence(sequence);
+
+            one(listener).afterActions(task);
+            inSequence(sequence);
+        }});
+
+        executer.execute(task, state);
+
+        assertThat(wrappedFailure.get(), instanceOf(TaskExecutionException.class));
+        TaskExecutionException exception = (TaskExecutionException) wrappedFailure.get();
+        assertThat(exception.getTask(), equalTo((Task) task));
+        assertThat(exception.getMessage(), equalTo("Execution failed for <task>."));
+        assertThat(exception.getCause(), sameInstance(failure));
+    }
+
+    @Test
+    public void stopsAtFirstActionWhichThrowsStopExecutionException() {
+        context.checking(new Expectations() {{
+            allowing(task).getActions();
+            will(returnValue(toList(action1, action2)));
+
+            one(listener).beforeActions(task);
+            inSequence(sequence);
+
+            one(state).setExecuting(true);
+            inSequence(sequence);
+
+            one(state).setDidWork(true);
+            inSequence(sequence);
+
+            one(standardOutputCapture).start();
+            inSequence(sequence);
+
+            one(action1).execute(task);
+            will(throwException(new StopExecutionException("stop")));
+            inSequence(sequence);
+
+            one(standardOutputCapture).stop();
+            inSequence(sequence);
+
+            one(state).executed(null);
+            inSequence(sequence);
+
+            one(state).setExecuting(false);
+            inSequence(sequence);
+
+            one(listener).afterActions(task);
+            inSequence(sequence);
+        }});
+
+        executer.execute(task, state);
+    }
+
+    @Test
+    public void skipsActionWhichThrowsStopActionException() {
+        context.checking(new Expectations() {{
+            allowing(task).getActions();
+            will(returnValue(toList(action1, action2)));
+
+            one(listener).beforeActions(task);
+            inSequence(sequence);
+
+            one(state).setExecuting(true);
+            inSequence(sequence);
+
+            one(state).setDidWork(true);
+            inSequence(sequence);
+
+            one(standardOutputCapture).start();
+            inSequence(sequence);
+
+            one(action1).execute(task);
+            will(throwException(new StopActionException("stop")));
+            inSequence(sequence);
+
+            one(standardOutputCapture).stop();
+            inSequence(sequence);
+
+            one(state).setDidWork(true);
+            inSequence(sequence);
+
+            one(standardOutputCapture).start();
+            inSequence(sequence);
+
+            one(action2).execute(task);
+            inSequence(sequence);
+
+            one(standardOutputCapture).stop();
+            inSequence(sequence);
+
+            one(state).executed(null);
+            inSequence(sequence);
+
+            one(state).setExecuting(false);
+            inSequence(sequence);
+
+            one(listener).afterActions(task);
+            inSequence(sequence);
+        }});
+
+        executer.execute(task, state);
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/ExecuteAtMostOnceTaskExecuterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/ExecuteAtMostOnceTaskExecuterTest.groovy
new file mode 100644
index 0000000..ecb7cb2
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/ExecuteAtMostOnceTaskExecuterTest.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.api.internal.tasks.execution
+
+import org.gradle.api.internal.TaskInternal
+import org.gradle.util.JUnit4GroovyMockery
+import org.jmock.integration.junit4.JMock
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.gradle.api.internal.tasks.TaskExecuter
+import org.gradle.api.internal.tasks.TaskStateInternal
+import static org.junit.Assert.*
+import static org.hamcrest.Matchers.*
+
+ at RunWith(JMock.class)
+class ExecuteAtMostOnceTaskExecuterTest {
+    private final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
+    private final TaskExecuter target = context.mock(TaskExecuter.class)
+    private final TaskInternal task = context.mock(TaskInternal.class)
+    private final TaskStateInternal state = context.mock(TaskStateInternal.class)
+    private final ExecuteAtMostOnceTaskExecuter executer = new ExecuteAtMostOnceTaskExecuter(target)
+
+    @Test
+    public void doesNothingWhenTaskHasAlreadyBeenExecuted() {
+        context.checking {
+            allowing(state).getExecuted()
+            will(returnValue(true))
+        }
+
+        executer.execute(task, state)
+    }
+
+    @Test
+    public void delegatesToExecuterWhenTaskHasNotBeenExecuted() {
+        context.checking {
+            allowing(state).getExecuted()
+            will(returnValue(false))
+            one(target).execute(task, state)
+            one(state).executed()
+        }
+
+        executer.execute(task, state)
+    }
+
+    @Test
+    public void marksTaskExecutedOnFailureFromExecuter() {
+        def failure = new RuntimeException()
+
+        context.checking {
+            allowing(state).getExecuted()
+            will(returnValue(false))
+            one(target).execute(task, state)
+            will(throwException(failure))
+            one(state).executed()
+        }
+
+        try {
+            executer.execute(task, state)
+            fail()
+        } catch (RuntimeException e) {
+            assertThat(e, sameInstance(failure))
+        }
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/PostExecutionAnalysisTaskExecuterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/PostExecutionAnalysisTaskExecuterTest.groovy
new file mode 100644
index 0000000..9054502
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/PostExecutionAnalysisTaskExecuterTest.groovy
@@ -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.internal.tasks.execution
+
+import org.gradle.api.Action
+import org.gradle.api.internal.TaskInternal
+import org.gradle.api.internal.tasks.TaskExecuter
+import org.gradle.api.internal.tasks.TaskStateInternal
+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 PostExecutionAnalysisTaskExecuterTest {
+    private final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
+    private final TaskExecuter target = context.mock(TaskExecuter.class)
+    private final TaskInternal task = context.mock(TaskInternal.class)
+    private final TaskStateInternal state = context.mock(TaskStateInternal.class)
+    private final PostExecutionAnalysisTaskExecuter executer = new PostExecutionAnalysisTaskExecuter(target)
+
+    @Test
+    public void marksTaskUpToDateWhenItHasActionsAndItDidNotDoWork() {
+        context.checking {
+            one(target).execute(task, state)
+            allowing(task).getActions();
+            will(returnValue([{} as Action]))
+            allowing(state).getDidWork()
+            will(returnValue(false))
+            one(state).upToDate()
+        }
+
+        executer.execute(task, state)
+    }
+
+    @Test
+    public void doesNotMarkTaskUpToDateWhenItHasActionsAndDidWork() {
+        context.checking {
+            one(target).execute(task, state)
+            allowing(task).getActions();
+            will(returnValue([{} as Action]))
+            allowing(state).getDidWork()
+            will(returnValue(true))
+        }
+
+        executer.execute(task, state)
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/SkipEmptySourceFilesTaskExecuterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/SkipEmptySourceFilesTaskExecuterTest.groovy
new file mode 100644
index 0000000..c6746c0
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/SkipEmptySourceFilesTaskExecuterTest.groovy
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.execution
+
+import org.gradle.api.file.FileCollection
+import org.gradle.api.internal.TaskInternal
+import org.gradle.api.internal.tasks.TaskExecuter
+import org.gradle.api.internal.tasks.TaskStateInternal
+import org.gradle.api.tasks.TaskInputs
+import spock.lang.Specification
+
+class SkipEmptySourceFilesTaskExecuterTest extends Specification {
+    final TaskExecuter target = Mock()
+    final TaskInternal task = Mock()
+    final TaskStateInternal state = Mock()
+    final TaskInputs taskInputs = Mock()
+    final FileCollection sourceFiles = Mock()
+    final SkipEmptySourceFilesTaskExecuter executer = new SkipEmptySourceFilesTaskExecuter(target)
+
+    def setup() {
+        _ * task.inputs >> taskInputs
+        _ * taskInputs.sourceFiles >> sourceFiles
+    }
+
+    def skipsTaskWhenItsSourceFilesCollectionIsEmpty() {
+        given:
+        taskInputs.hasSourceFiles >> true
+        sourceFiles.empty >> true
+
+        when:
+        executer.execute(task, state)
+
+        then:
+        1 * state.upToDate()
+        0 * target._
+        0 * state._
+    }
+
+    def executesTaskWhenItsSourceFilesCollectionIsNotEmpty() {
+        given:
+        taskInputs.hasSourceFiles >> true
+        sourceFiles.empty >> false
+
+        when:
+        executer.execute(task, state)
+
+        then:
+        1 * target.execute(task, state)
+        0 * target._
+        0 * state._
+    }
+
+    def executesTaskWhenTaskHasNotDeclaredAnySourceFiles() {
+        given:
+        taskInputs.hasSourceFiles >> false
+
+        when:
+        executer.execute(task, state)
+
+        then:
+        1 * target.execute(task, state)
+        0 * target._
+        0 * state._
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/SkipOnlyIfTaskExecuterTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/SkipOnlyIfTaskExecuterTest.java
new file mode 100644
index 0000000..80ab5cf
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/SkipOnlyIfTaskExecuterTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.execution;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.Task;
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.tasks.TaskExecuter;
+import org.gradle.api.internal.tasks.TaskStateInternal;
+import org.gradle.api.specs.Spec;
+import org.gradle.groovy.scripts.ScriptSource;
+import org.gradle.util.JUnit4GroovyMockery;
+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 static org.gradle.util.Matchers.*;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.assertThat;
+
+ at RunWith(JMock.class)
+public class SkipOnlyIfTaskExecuterTest {
+    private final JUnit4Mockery context = new JUnit4GroovyMockery();
+    private final TaskInternal task = context.mock(TaskInternal.class, "<task>");
+    private final Spec<Task> spec = context.mock(Spec.class);
+    private final TaskStateInternal state = context.mock(TaskStateInternal.class);
+    private final ScriptSource scriptSource = context.mock(ScriptSource.class);
+    private final TaskExecuter delegate = context.mock(TaskExecuter.class);
+    private final SkipOnlyIfTaskExecuter executer = new SkipOnlyIfTaskExecuter(delegate);
+
+    @Before
+    public void setUp() {
+        context.checking(new Expectations(){{
+            ProjectInternal project = context.mock(ProjectInternal.class);
+
+            allowing(task).getProject();
+            will(returnValue(project));
+
+            allowing(project).getBuildScriptSource();
+            will(returnValue(scriptSource));
+
+            allowing(task).getOnlyIf();
+            will(returnValue(spec));
+
+            ignoring(scriptSource);
+        }});
+    }
+
+    @Test
+    public void executesTask() {
+        context.checking(new Expectations() {{
+            allowing(task).getEnabled();
+            will(returnValue(true));
+
+            allowing(spec).isSatisfiedBy(task);
+            will(returnValue(true));
+
+            one(delegate).execute(task, state);
+        }});
+
+        executer.execute(task, state);
+    }
+
+    @Test
+    public void skipsTaskWhoseOnlyIfPredicateIsFalse() {
+        context.checking(new Expectations() {{
+            allowing(task).getEnabled();
+            will(returnValue(true));
+            one(spec).isSatisfiedBy(task);
+            will(returnValue(false));
+            one(state).skipped("SKIPPED");
+        }});
+
+        executer.execute(task, state);
+    }
+
+    @Test
+    public void wrapsOnlyIfPredicateFailure() {
+        final Throwable failure = new RuntimeException();
+        final Collector<Throwable> wrappedFailure = collector();
+        context.checking(new Expectations() {{
+            allowing(task).getEnabled();
+            will(returnValue(true));
+            one(spec).isSatisfiedBy(task);
+            will(throwException(failure));
+            one(state).executed(with(notNullValue(GradleException.class)));
+            will(collectTo(wrappedFailure));
+        }});
+
+        executer.execute(task, state);
+
+        GradleException exception = (GradleException) wrappedFailure.get();
+        assertThat(exception.getMessage(), equalTo("Could not evaluate onlyIf predicate for <task>."));
+        assertThat(exception.getCause(), sameInstance(failure));
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/SkipTaskWithNoActionsExecuterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/SkipTaskWithNoActionsExecuterTest.groovy
new file mode 100644
index 0000000..429f7ea
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/SkipTaskWithNoActionsExecuterTest.groovy
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.execution
+
+import org.gradle.api.internal.TaskInternal
+import org.gradle.api.tasks.TaskAction
+import org.gradle.api.tasks.TaskDependency
+import spock.lang.Specification
+import org.gradle.api.internal.tasks.TaskStateInternal
+import org.gradle.api.internal.tasks.TaskExecuter
+
+class SkipTaskWithNoActionsExecuterTest extends Specification {
+    final TaskInternal task = Mock()
+    final TaskStateInternal state = Mock()
+    final TaskExecuter target = Mock()
+    final TaskInternal dependency = Mock()
+    final TaskStateInternal dependencyState = Mock()
+    final SkipTaskWithNoActionsExecuter executor = new SkipTaskWithNoActionsExecuter(target)
+
+    def setup() {
+        TaskDependency taskDependency = Mock()
+        _ * task.taskDependencies >> taskDependency
+        _ * taskDependency.getDependencies(task) >> ([dependency] as Set)
+        _ * dependency.state >> dependencyState
+    }
+
+    def skipsTaskWithNoActionsAndMarksUpToDateIfAllItsDependenciesWereSkipped() {
+        given:
+        task.actions >> []
+        dependencyState.skipped >> true
+
+        when:
+        executor.execute(task, state)
+
+        then:
+        1 * state.upToDate()
+        0 * target._
+        0 * state._
+    }
+
+    def skipsTaskWithNoActionsAndMarksOutOfDateDateIfAnyOfItsDependenciesWereNotSkipped() {
+        given:
+        task.actions >> []
+        dependencyState.skipped >> false
+
+        when:
+        executor.execute(task, state)
+
+        then:
+        0 * target._
+        0 * state._
+    }
+
+    def executesTaskWithActions() {
+        given:
+        task.actions >> [{} as TaskAction]
+
+        when:
+        executor.execute(task, state)
+
+        then:
+        1 * target.execute(task, state)
+        0 * target._
+        0 * state._
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/SkipUpToDateTaskExecuterTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/SkipUpToDateTaskExecuterTest.java
new file mode 100644
index 0000000..61a5847
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/SkipUpToDateTaskExecuterTest.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.execution;
+
+import org.gradle.api.internal.TaskExecutionHistory;
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.TaskOutputsInternal;
+import org.gradle.api.internal.changedetection.TaskArtifactState;
+import org.gradle.api.internal.changedetection.TaskArtifactStateRepository;
+import org.gradle.api.internal.tasks.TaskExecuter;
+import org.gradle.api.internal.tasks.TaskStateInternal;
+import org.gradle.util.JUnit4GroovyMockery;
+import org.jmock.Expectations;
+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.runner.RunWith;
+
+ at RunWith(JMock.class)
+public class SkipUpToDateTaskExecuterTest {
+    private final JUnit4Mockery context = new JUnit4GroovyMockery();
+    private final TaskExecuter delegate = context.mock(TaskExecuter.class);
+    private final TaskOutputsInternal outputs = context.mock(TaskOutputsInternal.class);
+    private final TaskInternal task = context.mock(TaskInternal.class);
+    private final TaskStateInternal taskState = context.mock(TaskStateInternal.class);
+    private final TaskArtifactStateRepository repository = context.mock(TaskArtifactStateRepository.class);
+    private final TaskArtifactState taskArtifactState = context.mock(TaskArtifactState.class);
+    private final TaskExecutionHistory executionHistory = context.mock(TaskExecutionHistory.class);
+    private final SkipUpToDateTaskExecuter executer = new SkipUpToDateTaskExecuter(delegate, repository);
+
+    @Before
+    public void setup() {
+
+        context.checking(new Expectations(){{
+            allowing(task).getOutputs();
+            will(returnValue(outputs));
+        }});
+    }
+    @Test
+    public void skipsTaskWhenOutputsAreUpToDate() {
+        context.checking(new Expectations() {{
+            one(repository).getStateFor(task);
+            will(returnValue(taskArtifactState));
+
+            one(taskArtifactState).isUpToDate();
+            will(returnValue(true));
+
+            one(taskState).upToDate();
+
+            one(taskArtifactState).finished();
+        }});
+
+        executer.execute(task, taskState);
+    }
+    
+    @Test
+    public void executesTaskWhenOutputsAreNotUpToDate() {
+        context.checking(new Expectations() {{
+            Sequence sequence = context.sequence("seq");
+
+            one(repository).getStateFor(task);
+            will(returnValue(taskArtifactState));
+            inSequence(sequence);
+
+            one(taskArtifactState).isUpToDate();
+            will(returnValue(false));
+            inSequence(sequence);
+
+            one(taskArtifactState).beforeTask();
+            inSequence(sequence);
+
+            one(taskArtifactState).getExecutionHistory();
+            will(returnValue(executionHistory));
+
+            one(outputs).setHistory(executionHistory);
+            inSequence(sequence);
+
+            one(delegate).execute(task, taskState);
+            inSequence(sequence);
+
+            allowing(taskState).getFailure();
+            will(returnValue(null));
+
+            one(taskArtifactState).afterTask();
+            inSequence(sequence);
+
+            one(outputs).setHistory(null);
+            inSequence(sequence);
+
+            one(taskArtifactState).finished();
+            inSequence(sequence);
+        }});
+
+        executer.execute(task, taskState);
+    }
+
+    @Test
+    public void doesNotUpdateStateWhenTaskFails() {
+        context.checking(new Expectations() {{
+            one(repository).getStateFor(task);
+            will(returnValue(taskArtifactState));
+
+            one(taskArtifactState).isUpToDate();
+            will(returnValue(false));
+
+            one(taskArtifactState).beforeTask();
+
+            one(taskArtifactState).getExecutionHistory();
+            will(returnValue(executionHistory));
+
+            one(outputs).setHistory(executionHistory);
+
+            one(delegate).execute(task, taskState);
+
+            allowing(taskState).getFailure();
+            will(returnValue(new RuntimeException()));
+
+            one(outputs).setHistory(null);
+
+            one(taskArtifactState).finished();
+        }});
+
+        executer.execute(task, taskState);
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/ValidatingTaskExecuterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/ValidatingTaskExecuterTest.groovy
new file mode 100644
index 0000000..74e5113
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/ValidatingTaskExecuterTest.groovy
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.execution
+
+import spock.lang.Specification
+import org.gradle.api.internal.tasks.TaskStateInternal
+import org.gradle.api.internal.TaskInternal
+import org.gradle.api.internal.tasks.TaskExecuter
+import org.gradle.api.InvalidUserDataException
+import org.gradle.api.tasks.TaskValidationException
+
+class ValidatingTaskExecuterTest extends Specification {
+    final TaskExecuter target = Mock()
+    final TaskInternal task = Mock()
+    final TaskStateInternal state = Mock()
+    final TaskValidator validator = Mock()
+    final ValidatingTaskExecuter executer = new ValidatingTaskExecuter(target)
+
+    def executesTaskWhenThereAreNoViolations() {
+        when:
+        executer.execute(task, state)
+
+        then:
+        _ * task.validators >> [validator]
+        1 * validator.validate(task, !null)
+        1 * target.execute(task, state)
+        0 * _._
+    }
+
+    def failsTaskWhenThereIsAViolation() {
+        when:
+        executer.execute(task, state)
+
+        then:
+        _ * task.validators >> [validator]
+        1 * validator.validate(task, !null) >> { it[1] << 'failure' }
+        1 * state.executed(!null) >> {
+            def failure = it[0]
+            assert failure instanceof TaskValidationException
+            assert failure.message == "A problem was found with the configuration of $task."
+            assert failure.cause instanceof InvalidUserDataException
+            assert failure.cause.message == 'failure'
+        }
+        0 * _._
+    }
+
+    def failsTaskWhenThereAreMultipleViolations() {
+        when:
+        executer.execute(task, state)
+
+        then:
+        _ * task.validators >> [validator]
+        1 * validator.validate(task, !null) >> { it[1] << 'failure1'; it[1] << 'failure2' }
+        1 * state.executed(!null) >> {
+            def failure = it[0]
+            assert failure instanceof TaskValidationException
+            assert failure.message == "Some problems were found with the configuration of $task."
+            assert failure.causes[0] instanceof InvalidUserDataException
+            assert failure.causes[0].message == 'failure1'
+            assert failure.causes[1] instanceof InvalidUserDataException
+            assert failure.causes[1].message == 'failure2'
+        }
+        0 * _._
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/generator/PersistableConfigurationObjectGeneratorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/generator/PersistableConfigurationObjectGeneratorTest.groovy
deleted file mode 100644
index 7ae22ab..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/generator/PersistableConfigurationObjectGeneratorTest.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.internal.tasks.generator
-
-import spock.lang.Specification
-
-class PersistableConfigurationObjectGeneratorTest extends Specification {
-    final PersistableConfigurationObject object = Mock()
-    final PersistableConfigurationObjectGenerator<PersistableConfigurationObject> generator = new PersistableConfigurationObjectGenerator<PersistableConfigurationObject>() {
-        PersistableConfigurationObject create() {
-            return object
-        }
-        void configure(PersistableConfigurationObject object) {
-        }
-    }
-
-    def readsObjectFromFile() {
-        File inputFile = new File('input')
-
-        when:
-        def result = generator.read(inputFile)
-
-        then:
-        result == object
-        1 * object.load(inputFile)
-        0 * _._
-    }
-
-    def createsDefaultObject() {
-        when:
-        def result = generator.defaultInstance()
-
-        then:
-        result == object
-        1 * object.loadDefaults()
-        0 * _._
-    }
-
-    def writesObjectToFile() {
-        File outputFile = new File('output')
-
-        when:
-        generator.write(object, outputFile)
-
-        then:
-        1 * object.store(outputFile)
-        0 * _._
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/generator/PropertiesPersistableConfigurationObjectTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/generator/PropertiesPersistableConfigurationObjectTest.groovy
deleted file mode 100644
index 53f129e..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/generator/PropertiesPersistableConfigurationObjectTest.groovy
+++ /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.generator
-
-import org.gradle.util.Matchers
-import org.gradle.util.TemporaryFolder
-import org.junit.Rule
-import spock.lang.Specification
-
-class PropertiesPersistableConfigurationObjectTest extends Specification {
-    @Rule public final TemporaryFolder tmpDir = new TemporaryFolder()
-    String propertyValue
-    final PropertiesPersistableConfigurationObject object = new PropertiesPersistableConfigurationObject() {
-        @Override protected String getDefaultResourceName() {
-            return 'defaultResource.properties'
-        }
-
-        @Override protected void load(Properties properties) {
-            propertyValue = properties['prop']
-        }
-
-        @Override protected void store(Properties properties) {
-            properties['prop'] = propertyValue
-        }
-    }
-
-    def loadsFromPropertiesFile() {
-        def inputFile = tmpDir.file('input.properties')
-        inputFile.text = 'prop=value'
-
-        when:
-        object.load(inputFile)
-
-        then:
-        propertyValue == 'value'
-    }
-
-    def loadsFromDefaultResource() {
-        when:
-        object.loadDefaults()
-
-        then:
-        propertyValue == 'default-value'
-    }
-
-    def storesToXmlFile() {
-        object.loadDefaults()
-        propertyValue = 'modified-value'
-        def outputFile = tmpDir.file('output.properties')
-
-        when:
-        object.store(outputFile)
-
-        then:
-        Matchers.containsLine(outputFile.text, 'prop=modified-value')
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/generator/XmlPersistableConfigurationObjectTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/generator/XmlPersistableConfigurationObjectTest.groovy
deleted file mode 100644
index c757ddd..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/generator/XmlPersistableConfigurationObjectTest.groovy
+++ /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.internal.tasks.generator
-
-import org.gradle.api.internal.XmlTransformer
-import org.gradle.util.TemporaryFolder
-import org.gradle.util.TextUtil
-import org.junit.Rule
-import spock.lang.Specification
-
-class XmlPersistableConfigurationObjectTest extends Specification {
-    @Rule public final TemporaryFolder tmpDir = new TemporaryFolder()
-    String rootElement
-    final XmlPersistableConfigurationObject object = new XmlPersistableConfigurationObject(new XmlTransformer()) {
-        @Override protected String getDefaultResourceName() {
-            return 'defaultResource.xml'
-        }
-
-        @Override protected void load(Node xml) {
-            rootElement = xml.name() as String
-        }
-
-        @Override protected void store(Node xml) {
-            xml.name = rootElement
-        }
-    }
-
-    def loadsFromXmlFile() {
-        def inputFile = tmpDir.file('input.xml')
-        inputFile.text = '<some-xml/>'
-
-        when:
-        object.load(inputFile)
-
-        then:
-        rootElement == 'some-xml'
-    }
-
-    def loadsFromDefaultResource() {
-        when:
-        object.loadDefaults()
-
-        then:
-        rootElement == 'default-xml'
-    }
-
-    def storesToXmlFile() {
-        object.loadDefaults()
-        rootElement = 'modified-xml'
-        def outputFile = tmpDir.file('output.xml')
-
-        when:
-        object.store(outputFile)
-
-        then:
-        outputFile.text == TextUtil.toNativeLineSeparators('<?xml version="1.0" encoding="UTF-8"?>\n<modified-xml/>\n')
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/generator/defaultResource.xml b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/generator/defaultResource.xml
deleted file mode 100644
index b562ead..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/generator/defaultResource.xml
+++ /dev/null
@@ -1 +0,0 @@
-<default-xml/>
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/tasks/AntBuilderAwareUtil.groovy b/subprojects/core/src/test/groovy/org/gradle/api/tasks/AntBuilderAwareUtil.groovy
index fee9f81..ef8367d 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/tasks/AntBuilderAwareUtil.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/tasks/AntBuilderAwareUtil.groovy
@@ -25,6 +25,8 @@ import org.gradle.api.file.FileCollection
 import org.gradle.api.internal.project.DefaultAntBuilder
 import static org.hamcrest.Matchers.*
 import static org.junit.Assert.*
+import org.gradle.api.internal.file.collections.MinimalFileTree
+import org.gradle.api.internal.file.collections.FileTreeAdapter
 
 class AntBuilderAwareUtil {
 
@@ -60,10 +62,18 @@ class AntBuilderAwareUtil {
         assertSetContains(set, filenames as Set, FileCollection.AntType.values() as List)
     }
 
+    static def assertSetContainsForAllTypes(MinimalFileTree set, String ... filenames) {
+        assertSetContainsForAllTypes(new FileTreeAdapter(set), filenames)
+    }
+
     static def assertSetContainsForAllTypes(FileCollection set, Iterable<String> filenames) {
         assertSetContains(set, filenames as Set, FileCollection.AntType.values() as List)
     }
 
+    static def assertSetContainsForAllTypes(MinimalFileTree set, Iterable<String> filenames) {
+        assertSetContainsForAllTypes(new FileTreeAdapter(set), filenames)
+    }
+
     static def assertSetContainsForFileSet(FileCollection set, String ... filenames) {
         assertSetContains(set, filenames as Set, [FileCollection.AntType.FileSet], false)
     }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/tasks/CopyTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/tasks/CopyTest.groovy
index 75a7083..8d48ba2 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/tasks/CopyTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/tasks/CopyTest.groovy
@@ -1,85 +1,82 @@
-/*
- * 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
-
-import org.gradle.api.internal.AbstractTask
-import org.gradle.api.internal.file.DefaultDirectoryWalker
-
-import org.gradle.util.JUnit4GroovyMockery
-import org.jmock.lib.legacy.ClassImposteriser
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.gradle.api.internal.file.copy.FileCopyActionImpl
-
- at RunWith (org.jmock.integration.junit4.JMock)
-public class CopyTest extends AbstractTaskTest {
-    Copy copyTask;
-    DefaultDirectoryWalker walker;
-    FileCopyActionImpl action;
-
-    JUnit4GroovyMockery context = new JUnit4GroovyMockery();
-
-    @Before
-    public void setUp() {
-        super.setUp()
-        context.setImposteriser(ClassImposteriser.INSTANCE)
-        walker = context.mock(DefaultDirectoryWalker.class)
-        action = context.mock(FileCopyActionImpl.class)
-        copyTask = createTask(Copy.class)
-        copyTask.copyAction = action
-    }
-
-    public AbstractTask getTask() {
-        return copyTask;
-    }
-
-    @Test public void executesActionOnExecute() {
-        context.checking {
-            one(action).hasSource(); will(returnValue(true))
-            one(action).getDestinationDir(); will(returnValue(new File('dest')))
-            one(action).execute()
-            one(action).getDidWork()
-        }
-
-        copyTask.copy()
-    }
-    
-    @Test public void usesConventionValuesForDestDirWhenNotSpecified() {
-        copyTask.conventionMapping.destinationDir = { new File('convention') }
-
-        context.checking {
-            exactly(2).of(action).getDestinationDir()
-            will(returnValue(null))
-            one(action).into(new File('convention'))
-            one(action).hasSource(); will(returnValue(true))
-        }
-
-        copyTask.configureRootSpec()
-    }
-
-    @Test public void doesNotUseConventionValueForDestDirWhenSpecified() {
-        copyTask.conventionMapping.destinationDir = { new File('convention') }
-
-        context.checking {
-            one(action).getDestinationDir()
-            will(returnValue(new File('dest')))
-            one(action).hasSource(); will(returnValue(true))
-        }
-
-        copyTask.configureRootSpec()
-    }
-}
+/*
+ * 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
+
+import org.gradle.api.internal.AbstractTask
+
+import org.gradle.util.JUnit4GroovyMockery
+import org.jmock.lib.legacy.ClassImposteriser
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.gradle.api.internal.file.copy.FileCopyActionImpl
+
+ at RunWith (org.jmock.integration.junit4.JMock)
+public class CopyTest extends AbstractTaskTest {
+    Copy copyTask;
+    FileCopyActionImpl action;
+
+    JUnit4GroovyMockery context = new JUnit4GroovyMockery();
+
+    @Before
+    public void setUp() {
+        super.setUp()
+        context.setImposteriser(ClassImposteriser.INSTANCE)
+        action = context.mock(FileCopyActionImpl.class)
+        copyTask = createTask(Copy.class)
+        copyTask.copyAction = action
+    }
+
+    public AbstractTask getTask() {
+        return copyTask;
+    }
+
+    @Test public void executesActionOnExecute() {
+        context.checking {
+            one(action).hasSource(); will(returnValue(true))
+            one(action).getDestinationDir(); will(returnValue(new File('dest')))
+            one(action).execute()
+            one(action).getDidWork()
+        }
+
+        copyTask.copy()
+    }
+    
+    @Test public void usesConventionValuesForDestDirWhenNotSpecified() {
+        copyTask.conventionMapping.destinationDir = { new File('convention') }
+
+        context.checking {
+            exactly(2).of(action).getDestinationDir()
+            will(returnValue(null))
+            one(action).into(new File('convention'))
+            one(action).hasSource(); will(returnValue(true))
+        }
+
+        copyTask.configureRootSpec()
+    }
+
+    @Test public void doesNotUseConventionValueForDestDirWhenSpecified() {
+        copyTask.conventionMapping.destinationDir = { new File('convention') }
+
+        context.checking {
+            one(action).getDestinationDir()
+            will(returnValue(new File('dest')))
+            one(action).hasSource(); will(returnValue(true))
+        }
+
+        copyTask.configureRootSpec()
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/tasks/bundling/TarTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/tasks/bundling/TarTest.groovy
index 60f71d7..95edced 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/tasks/bundling/TarTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/tasks/bundling/TarTest.groovy
@@ -31,6 +31,7 @@ class TarTest extends AbstractArchiveTaskTest {
         super.setUp()
         tar = createTask(Tar)
         configure(tar)
+        tar.from tmpDir.createFile('file.txt')
     }
 
     AbstractArchiveTask getArchiveTask() {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/tasks/bundling/ZipTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/tasks/bundling/ZipTest.groovy
index 3864059..980edcd 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/tasks/bundling/ZipTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/tasks/bundling/ZipTest.groovy
@@ -30,6 +30,7 @@ class ZipTest extends AbstractArchiveTaskTest {
         super.setUp()
         zip = createTask(Zip)
         configure(zip)
+        zip.from tmpDir.createFile('file.txt')
     }
 
     AbstractArchiveTask getArchiveTask() {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/tasks/diagnostics/ProjectReportTaskTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/tasks/diagnostics/ProjectReportTaskTest.groovy
index 0f5e256..48c4435 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/tasks/diagnostics/ProjectReportTaskTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/tasks/diagnostics/ProjectReportTaskTest.groovy
@@ -82,8 +82,4 @@ 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/core/src/test/groovy/org/gradle/api/tasks/util/PatternSetTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/tasks/util/PatternSetTest.groovy
index 73bd602..2579129 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/tasks/util/PatternSetTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/tasks/util/PatternSetTest.groovy
@@ -23,6 +23,7 @@ import org.junit.Test
 import static org.gradle.util.Matchers.*
 import static org.hamcrest.Matchers.*
 import static org.junit.Assert.*
+import org.junit.After
 
 /**
 * @author Hans Dockter
@@ -34,6 +35,10 @@ class PatternSetTest extends AbstractTestForPatternSet {
         patternSet
     }
 
+    @After public void resetExcludes() {
+        PatternSet.resetGlobalExcludes()
+    }
+
     @Test public void testConstructionFromMap() {
         Map map = [includes: [TEST_PATTERN_1], excludes: [TEST_PATTERN_2]]
         PatternFilterable patternSet = new PatternSet(map)
@@ -54,7 +59,7 @@ class PatternSetTest extends AbstractTestForPatternSet {
         assertThat(new PatternSet(includes: ['i']), not(equalTo(new PatternSet(includes: ['other']))))
         assertThat(new PatternSet(excludes: ['e']), not(equalTo(new PatternSet(excludes: ['other']))))
     }
-    
+
     @Test public void canCopyFromAnotherPatternSet() {
         PatternSet other = new PatternSet()
         other.include 'a', 'b'
@@ -179,7 +184,7 @@ class PatternSetTest extends AbstractTestForPatternSet {
         assertFalse(spec.isSatisfiedBy(element(true, 'Ab')))
         assertTrue(spec.isSatisfiedBy(element(true, 'aB')))
     }
-    
+
     @Test public void createsSpecForCaseInsensitivePatternSet() {
         patternSet.include '*a*'
         patternSet.exclude '*b*'
diff --git a/subprojects/core/src/test/groovy/org/gradle/cache/DefaultCacheRepositoryTest.java b/subprojects/core/src/test/groovy/org/gradle/cache/DefaultCacheRepositoryTest.java
index 2f740ef..80708e5 100644
--- a/subprojects/core/src/test/groovy/org/gradle/cache/DefaultCacheRepositoryTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/cache/DefaultCacheRepositoryTest.java
@@ -44,7 +44,7 @@ public class DefaultCacheRepositoryTest {
     private final TestFile homeDir = tmpDir.createDir("home");
     private final TestFile buildRootDir = tmpDir.createDir("build");
     private final TestFile sharedCacheDir = homeDir.file("caches");
-    private final String version = new GradleVersion().getVersion();
+    private final String version = GradleVersion.current().getVersion();
     private final Map<String, ?> properties = GUtil.map("a", "value", "b", "value2");
     private final CacheFactory cacheFactory = context.mock(CacheFactory.class);
     private final PersistentCache cache = context.mock(PersistentCache.class);
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/TaskNameResolvingBuildExecuterTest.java b/subprojects/core/src/test/groovy/org/gradle/execution/TaskNameResolvingBuildExecuterTest.java
index b314e43..6382efa 100644
--- a/subprojects/core/src/test/groovy/org/gradle/execution/TaskNameResolvingBuildExecuterTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/TaskNameResolvingBuildExecuterTest.java
@@ -84,11 +84,11 @@ public class TaskNameResolvingBuildExecuterTest {
     @Test
     public void usesCamelCaseAbbreviationToSelectTasksWhenNoExactMatch() {
         assertMatches("soTaWN", "someTaskWithName", "saTaWN");
-        assertMatches("t1", "task1", "Task1", "T1", "t2");
+        assertMatches("ta1", "task1", "Task1", "T1", "t2");
         assertMatches("t1", "t1extra");
         assertMatches("t1", "t12");
         assertMatches("t1", "task1extra", "task2extra");
-        assertMatches("ABC", "AbcBbcCdc", "abc");
+        assertMatches("ABC", "AbcBbcCdc", "aabbcc");
         assertMatches("s-t", "some-task");
         assertMatches("s t", "some task");
         assertMatches("s.t", "some.task");
diff --git a/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/DefaultScriptCompilationHandlerTest.java b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/DefaultScriptCompilationHandlerTest.java
index 9cfeef3..0d6ff18 100644
--- a/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/DefaultScriptCompilationHandlerTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/DefaultScriptCompilationHandlerTest.java
@@ -31,12 +31,10 @@ import org.gradle.api.ScriptCompilationException;
 import org.gradle.api.internal.artifacts.dsl.AbstractScriptTransformer;
 import org.gradle.api.internal.resource.Resource;
 import org.gradle.util.TemporaryFolder;
-import static org.hamcrest.Matchers.*;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.integration.junit4.JUnit4Mockery;
 import org.junit.After;
-import static org.junit.Assert.*;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -45,6 +43,10 @@ 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.*;
+import static org.junit.Assert.*;
+
 /**
  * @author Hans Dockter
  */
@@ -207,20 +209,21 @@ public class DefaultScriptCompilationHandlerTest {
                     expectedScriptClass);
             fail();
         } catch (GradleException e) {
-            assertThat(e.getMessage(), equalTo("Could not load compiled classes for script-display-name from cache."));
+            assertThat(e.getMessage(), containsString("Could not load compiled classes for script-display-name from cache."));
             assertThat(e.getCause(), instanceOf(ClassCastException.class));
         }
     }
 
     @Test
-    public void testCompileToDirWithException() {
-        ScriptSource source = new StringScriptSource("script", "\n\nnew HHHHJSJSJ jsj");
+    public void testCompileToDirWithSyntaxError() {
+        ScriptSource source = new StringScriptSource("script.gradle", "\n\nnew HHHHJSJSJ jsj");
         try {
             scriptCompilationHandler.compileToDir(source, classLoader, scriptCacheDir, null, expectedScriptClass);
             fail();
         } catch (ScriptCompilationException e) {
             assertThat(e.getScriptSource(), sameInstance(source));
             assertThat(e.getLineNumber(), equalTo(3));
+            assertThat(e.getCause().getMessage(), containsLine(startsWith("script.gradle: 3: unexpected token: jsj")));
         }
 
         checkScriptCacheEmpty();
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/BuildProgressLoggerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/BuildProgressLoggerTest.groovy
index 6bcd471..22e769e 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/BuildProgressLoggerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/BuildProgressLoggerTest.groovy
@@ -34,31 +34,42 @@ class BuildProgressLoggerTest extends Specification {
     private final BuildProgressLogger logger = new BuildProgressLogger(progressLoggerFactory)
 
     def logsBuildStages() {
-        _ * gradle.getTaskGraph() >> graph
-        _ * result.getGradle() >> gradle
+        given:
+        gradle.getTaskGraph() >> graph
+        result.getGradle() >> gradle
 
         when:
         logger.buildStarted(gradle)
 
         then:
-        1 * progressLoggerFactory.start(BuildProgressLogger.name) >> progressLogger
-        1 * progressLogger.progress('Loading')
+        1 * progressLoggerFactory.newOperation(BuildProgressLogger) >> progressLogger
+        1 * progressLogger.setDescription('Configure projects')
+        1 * progressLogger.setShortDescription('Loading')
+        1 * progressLogger.started()
+        0 * progressLogger._
 
         when:
         logger.graphPopulated(graph)
 
         then:
-        1 * progressLogger.progress('Building')
+        1 * progressLogger.completed()
+        1 * progressLoggerFactory.newOperation(BuildProgressLogger) >> progressLogger
+        1 * progressLogger.setDescription('Execute tasks')
+        1 * progressLogger.setShortDescription('Building')
+        1 * progressLogger.started()
+        0 * progressLogger._
 
         when:
         logger.buildFinished(result)
 
         then:
         1 * progressLogger.completed()
+        0 * progressLogger._
     }
 
     def ignoresNestedBuilds() {
-        _ * gradle.getParent() >> Mock(Gradle)
+        given:
+        gradle.getParent() >> Mock(Gradle)
         
         when:
         logger.buildStarted(gradle)
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultExceptionAnalyserTest.java b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultExceptionAnalyserTest.java
index 4c40dbf..f9ac57b 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultExceptionAnalyserTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultExceptionAnalyserTest.java
@@ -15,28 +15,35 @@
  */
 package org.gradle.initialization;
 
+import org.gradle.api.GradleScriptException;
 import org.gradle.api.LocationAwareException;
 import org.gradle.api.internal.Contextual;
+import org.gradle.api.internal.MultiCauseException;
+import org.gradle.api.tasks.TaskExecutionException;
 import org.gradle.groovy.scripts.Script;
 import org.gradle.groovy.scripts.ScriptSource;
 import org.gradle.listener.ListenerManager;
-import org.gradle.util.WrapUtil;
+import org.gradle.listener.ListenerNotificationException;
+import org.gradle.util.JUnit4GroovyMockery;
 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 java.util.Arrays;
+import java.util.List;
+
+import static org.gradle.util.Matchers.isEmpty;
+import static org.gradle.util.WrapUtil.toArray;
+import static org.gradle.util.WrapUtil.toList;
 import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertThat;
 
 @RunWith(JMock.class)
 public class DefaultExceptionAnalyserTest {
-    private final JUnit4Mockery context = new JUnit4Mockery() {{
-        setImposteriser(ClassImposteriser.INSTANCE);
-    }};
+    private final JUnit4Mockery context = new JUnit4GroovyMockery();
     private final ListenerManager listenerManager = context.mock(ListenerManager.class);
     private final StackTraceElement element = new StackTraceElement("class", "method", "filename", 7);
     private final StackTraceElement callerElement = new StackTraceElement("class", "method", "filename", 11);
@@ -60,27 +67,40 @@ public class DefaultExceptionAnalyserTest {
     }
 
     @Test
-    public void wrapsContextualExceptionWithLocationInfoFromDeepestStackFrame() {
-        ContextualException failure = new ContextualException();
-        failure.setStackTrace(WrapUtil.toArray(element, otherElement, callerElement));
+    public void wrapsContextualExceptionWithLocationAwareException() {
+        Throwable failure = new ContextualException();
 
         DefaultExceptionAnalyser analyser = analyser();
-        notifyAnalyser(analyser, source);
 
         Throwable transformedFailure = analyser.transform(failure);
         assertThat(transformedFailure, instanceOf(LocationAwareException.class));
 
         LocationAwareException gse = (LocationAwareException) transformedFailure;
-        assertThat(gse.getScriptSource(), sameInstance(source));
-        assertThat(gse.getLineNumber(), equalTo(7));
+        assertThat(gse.getTarget(), sameInstance(failure));
+        assertThat(gse.getCause(), sameInstance(failure));
+        assertThat(gse.getReportableCauses(), isEmpty());
     }
 
     @Test
-    public void wrapsContextualExceptionWithLocationInfoFromDeepestCause() {
-        RuntimeException cause = new RuntimeException();
-        ContextualException failure = new ContextualException(new RuntimeException(cause));
-        failure.setStackTrace(WrapUtil.toArray(otherElement, callerElement));
-        cause.setStackTrace(WrapUtil.toArray(element, otherElement, callerElement));
+    public void wrapsDeepestContextualExceptionWithLocationAwareException() {
+        Throwable cause = new ContextualException();
+        Throwable failure = new ContextualException(new RuntimeException(cause));
+
+        DefaultExceptionAnalyser analyser = analyser();
+
+        Throwable transformedFailure = analyser.transform(failure);
+        assertThat(transformedFailure, instanceOf(LocationAwareException.class));
+
+        LocationAwareException gse = (LocationAwareException) transformedFailure;
+        assertThat(gse.getTarget(), sameInstance(cause));
+        assertThat(gse.getCause(), sameInstance(cause));
+        assertThat(gse.getReportableCauses(), isEmpty());
+    }
+
+    @Test
+    public void addsLocationInfoFromDeepestStackFrame() {
+        Throwable failure = new ContextualException();
+        failure.setStackTrace(toArray(element, otherElement, callerElement));
 
         DefaultExceptionAnalyser analyser = analyser();
         notifyAnalyser(analyser, source);
@@ -94,11 +114,11 @@ public class DefaultExceptionAnalyserTest {
     }
 
     @Test
-    public void wrapsDeepestContextualExceptionWithLocationInfo() {
-        ContextualException cause = new ContextualException();
+    public void addsLocationInfoFromDeepestCause() {
+        RuntimeException cause = new RuntimeException();
         ContextualException failure = new ContextualException(new RuntimeException(cause));
-        failure.setStackTrace(WrapUtil.toArray(otherElement, callerElement));
-        cause.setStackTrace(WrapUtil.toArray(element, otherElement, callerElement));
+        failure.setStackTrace(toArray(otherElement, callerElement));
+        cause.setStackTrace(toArray(element, otherElement, callerElement));
 
         DefaultExceptionAnalyser analyser = analyser();
         notifyAnalyser(analyser, source);
@@ -112,7 +132,7 @@ public class DefaultExceptionAnalyserTest {
     }
 
     @Test
-    public void wrapsOriginalExceptionWhenLocationCannotBeDetermined() {
+    public void doesNotAddLocationWhenLocationCannotBeDetermined() {
         Throwable failure = new ContextualException();
         Throwable transformedFailure = analyser().transform(failure);
         assertThat(transformedFailure, instanceOf(LocationAwareException.class));
@@ -123,14 +143,37 @@ public class DefaultExceptionAnalyserTest {
     }
 
     @Test
+    public void wrapsContextualMultiCauseExceptionWithLocationAwareException() {
+        Throwable cause1 = new ContextualException();
+        Throwable cause2 = new ContextualException();
+        Throwable failure = new ContextualMultiCauseException(cause1, cause2);
+
+        Throwable transformedFailure = analyser().transform(failure);
+        assertThat(transformedFailure, instanceOf(LocationAwareException.class));
+
+        LocationAwareException gse = (LocationAwareException) transformedFailure;
+        assertThat(gse.getTarget(), sameInstance(failure));
+        assertThat(gse.getCause(), sameInstance(failure));
+        assertThat(gse.getReportableCauses(), equalTo(toList(cause1, cause2)));
+    }
+
+    @Test
+    public void unpacksListenerNotificationException() {
+        Throwable cause = new RuntimeException();
+        Throwable failure = new ListenerNotificationException("broken", cause);
+
+        Throwable transformedFailure = analyser().transform(failure);
+        assertThat(transformedFailure, instanceOf(LocationAwareException.class));
+
+        LocationAwareException gse = (LocationAwareException) transformedFailure;
+        assertThat(gse.getTarget(), sameInstance(cause));
+        assertThat(gse.getCause(), sameInstance(failure));
+        assertThat(gse.getReportableCauses(), isEmpty());
+    }
+
+    @Test
     public void usesOriginalExceptionWhenItIsAlreadyLocationAware() {
-        final Throwable failure = context.mock(TestException.class);
-        context.checking(new Expectations() {{
-            allowing(failure).getCause();
-            will(returnValue(null));
-            allowing(failure).getStackTrace();
-            will(returnValue(WrapUtil.toArray(element)));
-        }});
+        Throwable failure = locationAwareException(null);
 
         DefaultExceptionAnalyser analyser = analyser();
         notifyAnalyser(analyser, source);
@@ -138,6 +181,63 @@ public class DefaultExceptionAnalyserTest {
         assertThat(analyser.transform(failure), sameInstance(failure));
     }
 
+    @Test
+    public void usesDeepestScriptExceptionException() {
+        Throwable cause = new GradleScriptException("broken", new RuntimeException());
+        Throwable failure = new GradleScriptException("broken", new RuntimeException(cause));
+
+        Throwable transformedFailure = analyser().transform(failure);
+        assertThat(transformedFailure, instanceOf(LocationAwareException.class));
+
+        LocationAwareException gse = (LocationAwareException) transformedFailure;
+        assertThat(gse.getTarget(), sameInstance(cause));
+        assertThat(gse.getCause(), sameInstance(cause));
+    }
+
+    @Test
+    public void usesDeepestLocationAwareException() {
+        Throwable cause = locationAwareException(null);
+        Throwable failure = locationAwareException(new RuntimeException(cause));
+
+        DefaultExceptionAnalyser analyser = analyser();
+
+        assertThat(analyser.transform(failure), sameInstance(cause));
+    }
+
+    @Test
+    public void prefersScriptExceptionOverContextualException() {
+        Throwable cause = new GradleScriptException("broken", new ContextualException());
+        Throwable failure = new TaskExecutionException(null, cause);
+
+        Throwable transformedFailure = analyser().transform(failure);
+        assertThat(transformedFailure, instanceOf(LocationAwareException.class));
+
+        LocationAwareException gse = (LocationAwareException) transformedFailure;
+        assertThat(gse.getTarget(), sameInstance(cause));
+        assertThat(gse.getCause(), sameInstance(cause));
+    }
+
+    @Test
+    public void prefersLocationAwareExceptionOverScriptException() {
+        Throwable cause = locationAwareException(new GradleScriptException("broken", new RuntimeException()));
+        Throwable failure = new TaskExecutionException(null, cause);
+
+        DefaultExceptionAnalyser analyser = analyser();
+
+        assertThat(analyser.transform(failure), sameInstance(cause));
+    }
+
+    private Throwable locationAwareException(final Throwable cause) {
+        final Throwable failure = context.mock(TestException.class);
+        context.checking(new Expectations() {{
+            allowing(failure).getCause();
+            will(returnValue(cause));
+            allowing(failure).getStackTrace();
+            will(returnValue(toArray(element)));
+        }});
+        return failure;
+    }
+
     private void notifyAnalyser(DefaultExceptionAnalyser analyser, final ScriptSource source) {
         final Script script = context.mock(Script.class);
         context.checking(new Expectations() {{
@@ -166,7 +266,22 @@ public class DefaultExceptionAnalyserTest {
     }
 
     @Contextual
-    public abstract static class TestException extends RuntimeException implements LocationAwareException {
+    public static class ContextualMultiCauseException extends RuntimeException implements MultiCauseException {
+        private List<Throwable> causes;
+
+        public ContextualMultiCauseException(Throwable... throwables) {
+            this.causes = Arrays.asList(throwables);
+        }
 
+        public List<? extends Throwable> getCauses() {
+            return causes;
+        }
+    }
+
+    @Contextual
+    public abstract static class TestException extends LocationAwareException {
+        protected TestException(Throwable cause, ScriptSource source, Integer lineNumber) {
+            super(cause, cause, source, lineNumber);
+        }
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/ExceptionDecoratingClassGeneratorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/ExceptionDecoratingClassGeneratorTest.groovy
deleted file mode 100644
index 64209d7..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/ExceptionDecoratingClassGeneratorTest.groovy
+++ /dev/null
@@ -1,123 +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 static org.junit.Assert.*
-import static org.hamcrest.Matchers.*
-import org.junit.Test
-import org.gradle.groovy.scripts.ScriptSource
-import org.gradle.api.GradleException
-import org.gradle.api.GradleScriptException
-import org.gradle.api.ScriptCompilationException
-import org.gradle.api.internal.Contextual
-import org.gradle.api.LocationAwareException
-
-public class ExceptionDecoratingClassGeneratorTest {
-    private final ScriptSource script = [getDisplayName: {"description"}] as ScriptSource
-    private final ExceptionDecoratingClassGenerator generator = new ExceptionDecoratingClassGenerator()
-
-    @Test
-    public void mixesLocationAwareIntoException() {
-        RuntimeException cause = new RuntimeException()
-        GradleException target = new GradleException("message", cause)
-
-        GradleException exception = generator.newInstance(GradleException.class, target, script, 12)
-        assertThat(exception.getClass().getPackage(), equalTo(GradleException.class.getPackage()))
-        assertThat(exception.getClass().simpleName, equalTo('LocationAwareGradleException'))
-        assertThat(exception, instanceOf(LocationAwareException))
-        assertThat(exception.originalMessage, equalTo("message"))
-        assertThat(exception.originalException, sameInstance(target))
-        assertThat(exception.cause, sameInstance(cause))
-        assertThat(exception.scriptSource, sameInstance(script))
-        assertThat(exception.lineNumber, equalTo(12))
-        assertThat(exception.stackTrace, equalTo(target.stackTrace))
-    }
-
-    @Test
-    public void cachesGeneratedType() {
-        assertSame(generator.generate(GradleException.class), generator.generate(GradleException.class))
-    }
-
-    @Test
-    public void messageIncludesSourceFileAndLineNumber() {
-        GradleException target = new GradleException("<message>")
-        GradleException exception = generator.newInstance(GradleException.class, target, script, 91)
-        assertThat(exception.location, equalTo("Description line: 91"));
-        assertThat(exception.message, equalTo(String.format("Description line: 91%n<message>")));
-    }
-
-    @Test
-    public void handlesExceptionWithNoLineNumber() {
-        GradleException target = new GradleException("<message>")
-        GradleException exception = generator.newInstance(GradleException.class, target, script, null)
-        assertThat(exception.location, equalTo("Description"));
-        assertThat(exception.message, equalTo(String.format("Description%n<message>")));
-    }
-
-    @Test
-    public void handlesExceptionWithNoLocation() {
-        GradleException target = new GradleException("<message>")
-        GradleException exception = generator.newInstance(GradleException.class, target, null, null)
-        assertThat(exception.location, nullValue());
-        assertThat(exception.message, equalTo("<message>"));
-    }
-
-    @Test
-    public void handlesExceptionWithNoMessage() {
-        GradleException target = new GradleException()
-        GradleException exception = generator.newInstance(GradleException.class, target, script, 91)
-        assertThat(exception.location, equalTo("Description line: 91"));
-        assertThat(exception.message, equalTo(String.format("Description line: 91")));
-    }
-
-    @Test
-    public void usesAllCauseExceptionsWhichAreContextualAsReportableCauses() {
-        RuntimeException actualCause = new RuntimeException(new Throwable());
-        ContextualException contextualCause = new ContextualException(actualCause);
-        ContextualException outerContextualCause = new ContextualException(contextualCause);
-        GradleException target = new GradleException('fail', outerContextualCause)
-        GradleException exception = generator.newInstance(GradleException.class, target, script, 91)
-        assertThat(exception.reportableCauses, equalTo([outerContextualCause, contextualCause, actualCause]));
-    }
-
-    @Test
-    public void usesDirectCauseAsReportableCauseWhenNoContextualCausesPresent() {
-        RuntimeException actualCause = new RuntimeException(new Throwable());
-        GradleException target = new GradleException('fail', actualCause)
-        GradleException exception = generator.newInstance(GradleException.class, target, script, 91)
-        assertThat(exception.reportableCauses, equalTo([actualCause]));
-    }
-
-    @Test
-    public void handlesTaskWithCopyConstructor() {
-        ScriptCompilationException target = new ScriptCompilationException("message", new RuntimeException(), script, 12)
-
-        GradleScriptException exception = generator.newInstance(ScriptCompilationException.class, target, null, null)
-        assertThat(exception.originalException, sameInstance(target))
-    }
-}
-
- at Contextual
-class ContextualException extends RuntimeException {
-    private ContextualException() {
-    }
-
-    private ContextualException(Throwable throwable) {
-        super(throwable);
-    }
-}
-
diff --git a/subprojects/core/src/test/groovy/org/gradle/invocation/DefaultGradleTest.java b/subprojects/core/src/test/groovy/org/gradle/invocation/DefaultGradleTest.java
index 1a24529..89b885f 100644
--- a/subprojects/core/src/test/groovy/org/gradle/invocation/DefaultGradleTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/invocation/DefaultGradleTest.java
@@ -100,7 +100,7 @@ public class DefaultGradleTest {
 
     @Test
     public void usesGradleVersion() {
-        assertThat(gradle.getGradleVersion(), equalTo(new GradleVersion().getVersion()));
+        assertThat(gradle.getGradleVersion(), equalTo(GradleVersion.current().getVersion()));
     }
 
     @Test
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/LoggingServiceRegistryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/LoggingServiceRegistryTest.groovy
index e95dae0..4c3f1cd 100644
--- a/subprojects/core/src/test/groovy/org/gradle/logging/LoggingServiceRegistryTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/LoggingServiceRegistryTest.groovy
@@ -22,10 +22,14 @@ import org.gradle.logging.internal.DefaultStyledTextOutputFactory
 import spock.lang.Specification
 import org.gradle.initialization.CommandLineConverter
 import org.gradle.logging.internal.LoggingCommandLineConverter
+import org.junit.Rule
+import org.gradle.util.RedirectStdOutAndErr
+import org.gradle.api.internal.project.ServiceRegistry
 
 class LoggingServiceRegistryTest extends Specification {
-    private final LoggingServiceRegistry registry = new LoggingServiceRegistry()
-    
+    @Rule RedirectStdOutAndErr outputs = new RedirectStdOutAndErr()
+    final ServiceRegistry registry = new LoggingServiceRegistry()
+
     def providesALoggingManagerFactory() {
         expect:
         def factory = registry.getFactory(LoggingManagerInternal.class)
@@ -49,4 +53,32 @@ class LoggingServiceRegistryTest extends Specification {
         def converter = registry.get(CommandLineConverter.class)
         converter instanceof LoggingCommandLineConverter
     }
+
+    def doesNotMessWithSystemOutAndErrUntilStarted() {
+        when:
+        def loggingManager = registry.newInstance(LoggingManagerInternal)
+
+        then:
+        System.out == outputs.stdOutPrintStream
+        System.err == outputs.stdErrPrintStream
+
+        when:
+        loggingManager.start()
+
+        then:
+        System.out != outputs.stdOutPrintStream
+        System.err != outputs.stdErrPrintStream
+    }
+    
+    def canDisableSystemOutAndErrCapture() {
+        def loggingManager = registry.newInstance(LoggingManagerInternal)
+        loggingManager.disableStandardOutputCapture()
+
+        when:
+        loggingManager.start()
+
+        then:
+        System.out == outputs.stdOutPrintStream
+        System.err == outputs.stdErrPrintStream
+    }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/internal/AbstractStyledTextOutputTest.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/internal/AbstractStyledTextOutputTest.groovy
index 6ff0322..470998e 100644
--- a/subprojects/core/src/test/groovy/org/gradle/logging/internal/AbstractStyledTextOutputTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/internal/AbstractStyledTextOutputTest.groovy
@@ -16,6 +16,7 @@
 package org.gradle.logging.internal
 
 import org.gradle.logging.StyledTextOutput.Style
+import org.gradle.util.SystemProperties
 
 class AbstractStyledTextOutputTest extends OutputSpecification {
     private final TestStyledTextOutput output = new TestStyledTextOutput()
@@ -41,7 +42,7 @@ class AbstractStyledTextOutputTest extends OutputSpecification {
         output.println()
 
         then:
-        output.rawValue == System.getProperty('line.separator')
+        output.rawValue == SystemProperties.lineSeparator
     }
 
     def appendsCharacter() {
@@ -201,7 +202,7 @@ class TestStyledTextOutput extends AbstractStyledTextOutput {
     def String getValue() {
         StringBuilder normalised = new StringBuilder()
 
-        String eol = System.getProperty('line.separator')
+        String eol = SystemProperties.lineSeparator
         boolean inStackTrace = false
         new StringTokenizer(result.toString().replaceAll(eol, '\n'), '\n', true).each { String line ->
             if (line == '\n') {
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/internal/AnsiConsoleTest.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/internal/AnsiConsoleTest.groovy
index e24efa4..d776767 100644
--- a/subprojects/core/src/test/groovy/org/gradle/logging/internal/AnsiConsoleTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/internal/AnsiConsoleTest.groovy
@@ -17,6 +17,7 @@ package org.gradle.logging.internal
 
 import org.fusesource.jansi.Ansi
 import org.fusesource.jansi.Ansi.Color
+import org.gradle.util.SystemProperties
 import org.gradle.logging.StyledTextOutput
 import org.gradle.logging.StyledTextOutput.Style
 import org.gradle.util.JUnit4GroovyMockery
@@ -26,7 +27,8 @@ import org.junit.runner.RunWith
 
 @RunWith(JMock.class)
 class AnsiConsoleTest {
-    private static final String EOL = System.getProperty('line.separator')
+    private static final String EOL = SystemProperties.lineSeparator
+
     private final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
     private final Ansi ansi = context.mock(Ansi.class)
     private final Appendable target = {} as Appendable
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/internal/ConsoleBackedProgressRendererTest.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/internal/ConsoleBackedProgressRendererTest.groovy
index b97ed50..3e131cd 100644
--- a/subprojects/core/src/test/groovy/org/gradle/logging/internal/ConsoleBackedProgressRendererTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/internal/ConsoleBackedProgressRendererTest.groovy
@@ -37,11 +37,51 @@ class ConsoleBackedProgressRendererTest extends OutputSpecification {
         0 * statusBar._
     }
 
-    def statusBarTracksOperationProgress() {
+    def statusBarTracksMostRecentOperationStatus() {
         when:
-        renderer.onOutput(start('description'))
+        renderer.onOutput(start(status: 'status'))
 
         then:
+        1 * statusBar.setText('> status')
+        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 statusBarTracksOperationProgressForOperationWithNoStatus() {
+        when:
+        renderer.onOutput(start(status: ''))
+
+        then:
+        1 * statusBar.setText('')
+        0 * statusBar._
+
+        when:
+        renderer.onOutput(complete('complete'))
+
+        then:
+        1 * statusBar.setText('')
+        0 * statusBar._
+    }
+
+    def statusBarTracksOperationProgressForOperationWithNoInitialStatus() {
+        when:
+        renderer.onOutput(start(status: ''))
+
+        then:
+        1 * statusBar.setText('')
         0 * statusBar._
 
         when:
@@ -61,9 +101,10 @@ class ConsoleBackedProgressRendererTest extends OutputSpecification {
 
     def statusBarTracksNestedOperationProgress() {
         when:
-        renderer.onOutput(start('description'))
+        renderer.onOutput(start(status: 'status'))
 
         then:
+        1 * statusBar.setText('> status')
         0 * statusBar._
 
         when:
@@ -74,9 +115,10 @@ class ConsoleBackedProgressRendererTest extends OutputSpecification {
         0 * statusBar._
 
         when:
-        renderer.onOutput(start('description2'))
+        renderer.onOutput(start(status: 'status2'))
 
         then:
+        1 * statusBar.setText('> progress > status2')
         0 * statusBar._
 
         when:
@@ -101,12 +143,13 @@ class ConsoleBackedProgressRendererTest extends OutputSpecification {
         0 * statusBar._
     }
 
-    def statusBarTracksNestedOperationProgressForOperationsWithNoStatus() {
+    def statusBarTracksNestedOperationProgressForOperationsWithNoInitialStatus() {
         when:
-        renderer.onOutput(start('description'))
-        renderer.onOutput(start('description2'))
+        renderer.onOutput(start(status: ''))
+        renderer.onOutput(start(status: ''))
 
         then:
+        2 * statusBar.setText('')
         0 * statusBar._
 
         when:
@@ -130,4 +173,34 @@ class ConsoleBackedProgressRendererTest extends OutputSpecification {
         1 * statusBar.setText('')
         0 * statusBar._
     }
+
+    def usesShortDescriptionWhenOperationHasNoStatus() {
+        when:
+        renderer.onOutput(start(shortDescription: 'short'))
+
+        then:
+        1 * statusBar.setText('> short')
+        0 * statusBar._
+
+        when:
+        renderer.onOutput(progress('progress'))
+
+        then:
+        1 * statusBar.setText('> progress')
+        0 * statusBar._
+
+        when:
+        renderer.onOutput(progress(''))
+
+        then:
+        1 * statusBar.setText('> short')
+        0 * statusBar._
+
+        when:
+        renderer.onOutput(complete('complete'))
+
+        then:
+        1 * statusBar.setText('')
+        0 * statusBar._
+    }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/internal/DefaultLoggingManagerTest.java b/subprojects/core/src/test/groovy/org/gradle/logging/internal/DefaultLoggingManagerTest.java
index b3dea4b..d981bdb 100644
--- a/subprojects/core/src/test/groovy/org/gradle/logging/internal/DefaultLoggingManagerTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/internal/DefaultLoggingManagerTest.java
@@ -287,25 +287,43 @@ public class DefaultLoggingManagerTest {
     }
 
     @Test
-    public void addsListenersOnStartAndRemovesOnStop() {
+    public void addsStdOutputListenerOnStartAndRemovesOnStop() {
         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);
+        }});
+
+        loggingManager.stop();
+    }
+
+    @Test
+    public void addsStdErrorListenerOnStartAndRemovesOnStop() {
+        final StandardOutputListener stderrListener = context.mock(StandardOutputListener.class);
+
+        loggingManager.addStandardErrorListener(stderrListener);
+
+        context.checking(new Expectations() {{
+            ignoring(loggingSystem);
+            ignoring(stdOutLoggingSystem);
+            ignoring(stdErrLoggingSystem);
+            one(loggingOutput).addStandardErrorListener(stderrListener);
+        }});
+
+        loggingManager.start();
+
+        context.checking(new Expectations() {{
             one(loggingOutput).removeStandardErrorListener(stderrListener);
         }});
 
@@ -313,9 +331,30 @@ public class DefaultLoggingManagerTest {
     }
 
     @Test
-    public void addsListenersWhileStarted() {
+    public void addsOutputEventListenerOnStartAndRemovesOnStop() {
+        final OutputEventListener listener = context.mock(OutputEventListener.class);
+
+        loggingManager.addOutputEventListener(listener);
+
+        context.checking(new Expectations() {{
+            ignoring(loggingSystem);
+            ignoring(stdOutLoggingSystem);
+            ignoring(stdErrLoggingSystem);
+            one(loggingOutput).addOutputEventListener(listener);
+        }});
+
+        loggingManager.start();
+
+        context.checking(new Expectations() {{
+            one(loggingOutput).removeOutputEventListener(listener);
+        }});
+
+        loggingManager.stop();
+    }
+
+    @Test
+    public void addsStdOutputListenerWhileStarted() {
         final StandardOutputListener stdoutListener = context.mock(StandardOutputListener.class);
-        final StandardOutputListener stderrListener = context.mock(StandardOutputListener.class);
 
         context.checking(new Expectations() {{
             ignoring(loggingSystem);
@@ -327,37 +366,136 @@ public class DefaultLoggingManagerTest {
 
         context.checking(new Expectations() {{
             one(loggingOutput).addStandardOutputListener(stdoutListener);
-            one(loggingOutput).addStandardErrorListener(stderrListener);
         }});
 
         loggingManager.addStandardOutputListener(stdoutListener);
+
+        context.checking(new Expectations() {{
+            one(loggingOutput).removeStandardOutputListener(stdoutListener);
+        }});
+
+        loggingManager.stop();
+    }
+
+    @Test
+    public void addsStdErrorListenerWhileStarted() {
+        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).addStandardErrorListener(stderrListener);
+        }});
+
         loggingManager.addStandardErrorListener(stderrListener);
+
+        context.checking(new Expectations() {{
+            one(loggingOutput).removeStandardErrorListener(stderrListener);
+        }});
+
+        loggingManager.stop();
+    }
+
+    @Test
+    public void addsOutputEventListenerWhileStarted() {
+        final OutputEventListener listener = context.mock(OutputEventListener.class);
+
+        context.checking(new Expectations() {{
+            ignoring(loggingSystem);
+            ignoring(stdOutLoggingSystem);
+            ignoring(stdErrLoggingSystem);
+        }});
+
+        loggingManager.start();
+
+        context.checking(new Expectations() {{
+            one(loggingOutput).addOutputEventListener(listener);
+        }});
+
+        loggingManager.addOutputEventListener(listener);
+
+        context.checking(new Expectations() {{
+            one(loggingOutput).removeOutputEventListener(listener);
+        }});
+
+        loggingManager.stop();
     }
 
     @Test
-    public void removesListenersWhileStarted() {
+    public void removesStdOutputListenerWhileStarted() {
         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.stop();
+    }
+
+    @Test
+    public void removesStdErrorListenerWhileStarted() {
+        final StandardOutputListener stderrListener = context.mock(StandardOutputListener.class);
+
+        loggingManager.addStandardErrorListener(stderrListener);
+
+        context.checking(new Expectations() {{
+            ignoring(loggingSystem);
+            ignoring(stdOutLoggingSystem);
+            ignoring(stdErrLoggingSystem);
+            one(loggingOutput).addStandardErrorListener(stderrListener);
+        }});
+
+        loggingManager.start();
+
+        context.checking(new Expectations() {{
+            one(loggingOutput).removeStandardErrorListener(stderrListener);
+        }});
+
         loggingManager.removeStandardErrorListener(stderrListener);
+
+        loggingManager.stop();
+    }
+
+    @Test
+    public void removesOutputEventListenerWhileStarted() {
+        final OutputEventListener listener = context.mock(OutputEventListener.class);
+
+        loggingManager.addOutputEventListener(listener);
+
+        context.checking(new Expectations() {{
+            ignoring(loggingSystem);
+            ignoring(stdOutLoggingSystem);
+            ignoring(stdErrLoggingSystem);
+            one(loggingOutput).addOutputEventListener(listener);
+        }});
+
+        loggingManager.start();
+
+        context.checking(new Expectations() {{
+            one(loggingOutput).removeOutputEventListener(listener);
+        }});
+
+        loggingManager.removeOutputEventListener(listener);
+
+        loggingManager.stop();
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/internal/DefaultProgressLoggerFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/internal/DefaultProgressLoggerFactoryTest.groovy
index 9d7e5b1..0a63d83 100644
--- a/subprojects/core/src/test/groovy/org/gradle/logging/internal/DefaultProgressLoggerFactoryTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/internal/DefaultProgressLoggerFactoryTest.groovy
@@ -26,53 +26,216 @@ class DefaultProgressLoggerFactoryTest extends Specification {
 
     def progressLoggerBroadcastsEvents() {
         when:
-        def logger = factory.start('logger', 'description')
+        def logger = factory.newOperation('logger')
+        logger.description = 'description'
+        logger.started('started')
 
         then:
         logger != null
         1 * timeProvider.getCurrentTime() >> 100L
-        1 * progressListener.started({it.timestamp == 100L && it.category == 'logger' && it.description == 'description'})
+        1 * progressListener.started(!null) >> { args ->
+            def event = args[0]
+            assert event.timestamp == 100L
+            assert event.category == 'logger'
+            assert event.description == 'description'
+            assert event.shortDescription == null
+            assert event.loggingHeader == null
+            assert event.status == 'started'
+        }
 
         when:
         logger.progress('progress')
 
         then:
         1 * timeProvider.getCurrentTime() >> 200L
-        1 * progressListener.progress({it.timestamp == 200L && it.category == 'logger' && it.status == 'progress'})
+        1 * progressListener.progress(!null) >> { args ->
+            def event = args[0]
+            assert event.timestamp == 200L
+            assert event.category == 'logger'
+            assert event.status == 'progress'
+        }
 
         when:
         logger.completed('completed')
 
         then:
         1 * timeProvider.getCurrentTime() >> 300L
-        1 * progressListener.completed({it.timestamp == 300L && it.category == 'logger' && it.status == 'completed'})
+        1 * progressListener.completed(!null) >> { args ->
+            def event = args[0]
+            assert event.timestamp == 300L
+            assert event.category == 'logger'
+            assert event.status == 'completed'
+        }
     }
 
-    def hasEmptyStatusOnStart() {
+    def canSpecifyShortDescription() {
         when:
-        def logger = factory.start('logger', 'description')
+        def logger = factory.newOperation('logger')
+        logger.description = 'description'
+        logger.shortDescription = 'short'
+        logger.started()
 
         then:
-        logger.description == 'description'
-        logger.status == ''
+        1 * progressListener.started(!null) >> { args ->
+            def event = args[0]
+            assert event.shortDescription == 'short'
+        }
     }
 
-    def hasMostRecentStatusOnProgress() {
+    def canSpecifyLoggingHeader() {
         when:
-        def logger = factory.start('logger', 'description')
-        logger.progress('status')
+        def logger = factory.newOperation('logger')
+        logger.description = 'description'
+        logger.loggingHeader = 'header'
+        logger.started()
 
         then:
-        logger.status == 'status'
+        1 * progressListener.started(!null) >> { args ->
+            def event = args[0]
+            assert event.loggingHeader == 'header'
+        }
     }
-    
-    def hasMostRecentStatusOnComplete() {
+
+    def canSpecifyNullStatus() {
+        def logger = factory.newOperation('logger')
+        logger.description = 'not empty'
+
+        when:
+        logger.started(null)
+        logger.progress(null)
+        logger.completed(null)
+
+        then:
+        1 * progressListener.started({it.status == ''})
+        1 * progressListener.progress({it.status == ''})
+        1 * progressListener.completed({it.status == ''})
+    }
+
+    def mustSpecifyDescriptionBeforeStart() {
+        def logger = factory.newOperation('logger')
+
+        when:
+        logger.started()
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'A description must be specified before this operation is started.'
+    }
+
+    def cannotChangeDescriptionAfterStart() {
+        def logger = factory.newOperation('logger')
+        logger.description = 'old'
+        logger.started()
+
+        when:
+        logger.description = 'new'
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'Cannot configure this operation once it has started.'
+    }
+
+    def cannotChangeShortDescriptionAfterStart() {
+        def logger = factory.newOperation('logger')
+        logger.description = 'old'
+        logger.started()
+
+        when:
+        logger.shortDescription = 'new'
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'Cannot configure this operation once it has started.'
+    }
+
+    def cannotChangeLoggingHeaderAfterStart() {
+        def logger = factory.newOperation('logger')
+        logger.description = 'old'
+        logger.started()
+
+        when:
+        logger.loggingHeader = 'new'
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'Cannot configure this operation once it has started.'
+    }
+
+    def cannotMakeProgressBeforeStart() {
+        def logger = factory.newOperation('logger')
+
+        when:
+        logger.progress('new')
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'This operation has not been started.'
+    }
+
+    def cannotMakeProgressAfterCompletion() {
+        def logger = factory.newOperation('logger')
+        logger.description = 'not empty'
+        logger.started()
+        logger.completed()
+
+        when:
+        logger.progress('new')
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'This operation has completed.'
+    }
+
+    def cannotCompleteBeforeStart() {
+        def logger = factory.newOperation('logger')
+
+        when:
+        logger.completed('finished')
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'This operation has not been started.'
+    }
+
+    def cannotStartMultipleTimes() {
+        def logger = factory.newOperation('logger')
+        logger.description = 'not empty'
+        logger.started()
+
+        when:
+        logger.started()
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'This operation has already been started.'
+    }
+
+    def cannotStartAfterComplete() {
+        def logger = factory.newOperation('logger')
+        logger.description = 'not empty'
+        logger.started()
+        logger.completed()
+
+        when:
+        logger.started()
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'This operation has completed.'
+    }
+
+    def cannotCompleteMultipleTimes() {
+        def logger = factory.newOperation('logger')
+        logger.description = 'not empty'
+        logger.started()
+        logger.completed()
+
         when:
-        def logger = factory.start('logger', 'description')
-        logger.completed('done')
+        logger.completed()
 
         then:
-        logger.status == 'done'
+        IllegalStateException e = thrown()
+        e.message == 'This operation has completed.'
     }
 }
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/internal/DefaultStandardOutputRedirectorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/internal/DefaultStandardOutputRedirectorTest.groovy
index e927d69..73ca11e 100644
--- a/subprojects/core/src/test/groovy/org/gradle/logging/internal/DefaultStandardOutputRedirectorTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/internal/DefaultStandardOutputRedirectorTest.groovy
@@ -16,12 +16,14 @@
 package org.gradle.logging.internal
 
 import org.gradle.api.logging.StandardOutputListener
+import org.gradle.util.SystemProperties
 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')
+    private static final String EOL = SystemProperties.lineSeparator
+
     @Rule public final RedirectStdOutAndErr outputs = new RedirectStdOutAndErr()
     private final DefaultStandardOutputRedirector redirector = new DefaultStandardOutputRedirector()
     private final StandardOutputListener stdOutListener = Mock()
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/internal/OutputEventRendererTest.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/internal/OutputEventRendererTest.groovy
index f28c48c..9b28b73 100644
--- a/subprojects/core/src/test/groovy/org/gradle/logging/internal/OutputEventRendererTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/internal/OutputEventRendererTest.groovy
@@ -19,6 +19,7 @@ import org.gradle.api.logging.LogLevel
 import org.gradle.api.logging.StandardOutputListener
 import org.gradle.util.RedirectStdOutAndErr
 import org.junit.Rule
+import org.gradle.api.specs.Spec
 
 class OutputEventRendererTest extends OutputSpecification {
     @Rule public final RedirectStdOutAndErr outputs = new RedirectStdOutAndErr()
@@ -26,7 +27,7 @@ class OutputEventRendererTest extends OutputSpecification {
     private OutputEventRenderer renderer
 
     def setup() {
-        renderer = new OutputEventRenderer()
+        renderer = new OutputEventRenderer(Mock(Spec))
         renderer.addStandardOutput(outputs.stdOutPrintStream)
         renderer.addStandardError(outputs.stdErrPrintStream)
         renderer.configure(LogLevel.INFO)
@@ -166,7 +167,7 @@ class OutputEventRendererTest extends OutputSpecification {
 
     def rendersProgressEvents() {
         when:
-        renderer.onOutput(start('description'))
+        renderer.onOutput(start(loggingHeader: 'description'))
         renderer.onOutput(complete('status'))
 
         then:
@@ -189,7 +190,7 @@ class OutputEventRendererTest extends OutputSpecification {
         renderer.addConsole(console, true, true)
 
         when:
-        renderer.onOutput(start('description'))
+        renderer.onOutput(start(loggingHeader: 'description'))
         renderer.onOutput(event('info', LogLevel.INFO))
         renderer.onOutput(event('error', LogLevel.ERROR))
         renderer.onOutput(complete('status'))
@@ -202,7 +203,7 @@ class OutputEventRendererTest extends OutputSpecification {
         renderer.addConsole(console, true, false)
 
         when:
-        renderer.onOutput(start('description'))
+        renderer.onOutput(start(loggingHeader: 'description'))
         renderer.onOutput(event('info', LogLevel.INFO))
         renderer.onOutput(event('error', LogLevel.ERROR))
         renderer.onOutput(complete('status'))
@@ -225,7 +226,7 @@ class OutputEventRendererTest extends OutputSpecification {
     }
 }
 
-private static class TestListener implements StandardOutputListener {
+class TestListener implements StandardOutputListener {
     private final StringWriter writer = new StringWriter();
 
     def getValue() {
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/internal/OutputSpecification.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/internal/OutputSpecification.groovy
index 96e04bc..5cb6cc9 100644
--- a/subprojects/core/src/test/groovy/org/gradle/logging/internal/OutputSpecification.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/internal/OutputSpecification.groovy
@@ -17,54 +17,59 @@ package org.gradle.logging.internal
 
 import spock.lang.Specification
 import org.gradle.api.logging.LogLevel
+import org.gradle.util.TextUtil
 import java.text.SimpleDateFormat
 
 class OutputSpecification extends Specification {
-    def String toNative(String value) {
-        return value.replaceAll('\n', System.getProperty('line.separator'))
+    protected String toNative(String value) {
+        return TextUtil.toPlatformLineSeparators(value)
     }
 
     /**
      * Returns timestamp representing 10AM today in local time.
      */
-    def long getTenAm() {
+    long getTenAm() {
         return getTime('10:00:00.000')
     }
 
-    def long getTime(String time) {
+    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) {
+    LogEvent event(String text) {
         return new LogEvent(tenAm, 'category', LogLevel.INFO, text, null)
     }
 
-    def LogEvent event(String text, LogLevel logLevel) {
+    LogEvent event(String text, LogLevel logLevel) {
         return new LogEvent(tenAm, 'category', logLevel, text, null)
     }
 
-    def LogEvent event(long timestamp, String text, LogLevel logLevel) {
+    LogEvent event(long timestamp, String text, LogLevel logLevel) {
         return new LogEvent(timestamp, 'category', logLevel, text, null)
     }
 
-    def LogEvent event(long timestamp, String text) {
+    LogEvent event(long timestamp, String text) {
         return new LogEvent(timestamp, 'category', LogLevel.INFO, text, null)
     }
 
-    def LogEvent event(String text, Throwable throwable) {
+    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)
+    ProgressStartEvent start(String description) {
+        return new ProgressStartEvent(tenAm, 'category', description, null, null, null)
     }
 
-    def ProgressEvent progress(String status) {
+    ProgressStartEvent start(Map args) {
+        return new ProgressStartEvent(tenAm, 'category', args.description, args.shortDescription, args.loggingHeader, args.status)
+    }
+
+    ProgressEvent progress(String status) {
         return new ProgressEvent(tenAm, 'category', status)
     }
 
-    def ProgressCompleteEvent complete(String status) {
+    ProgressCompleteEvent complete(String status) {
         return new ProgressCompleteEvent(tenAm, 'category', status)
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/internal/ProgressLogEventGeneratorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/internal/ProgressLogEventGeneratorTest.groovy
index 8a10801..593ebb7 100644
--- a/subprojects/core/src/test/groovy/org/gradle/logging/internal/ProgressLogEventGeneratorTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/internal/ProgressLogEventGeneratorTest.groovy
@@ -18,16 +18,16 @@ package org.gradle.logging.internal
 class ProgressLogEventGeneratorTest extends OutputSpecification {
     private final OutputEventListener target = Mock()
 
-    def insertsLogMessagesWhenOperationStartsAndEnds() {
+    def insertsLogHeaderForOperation() {
         ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, false)
-        def startEvent = start('description')
+        def startEvent = start(loggingHeader: 'description')
         def completeEvent = complete('status')
 
         when:
         generator.onOutput(startEvent)
 
         then:
-        1 * target.onOutput(!null) >> { args->
+        1 * target.onOutput(!null) >> { args ->
             StyledTextOutputEvent event = args[0]
             assert event.spans[0].text == 'description'
             assert event.timestamp == startEvent.timestamp
@@ -49,9 +49,9 @@ class ProgressLogEventGeneratorTest extends OutputSpecification {
         0 * target._
     }
 
-    def insertsLogMessagesWhenOperationStartsAndEndsAndDeferredHeaderMode() {
+    def insertsLogHeaderForOperationAndDeferredHeaderMode() {
         ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, true)
-        def startEvent = start('description')
+        def startEvent = startWithHeader('description')
         def completeEvent = complete('status')
 
         when:
@@ -75,15 +75,15 @@ class ProgressLogEventGeneratorTest extends OutputSpecification {
         0 * target._
     }
 
-    def insertsLogMessagesWhenOperationGeneratesSomeOutput() {
+    def insertsLogHeaderAndFooterWhenOperationGeneratesSomeOutput() {
         ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, false)
         def logEvent = event('message')
 
         when:
-        generator.onOutput(start('description'))
+        generator.onOutput(startWithHeader('description'))
 
         then:
-        1 * target.onOutput(!null) >> { args->
+        1 * target.onOutput(!null) >> { args ->
             StyledTextOutputEvent event = args[0]
             assert event.spans.size() == 1
             assert event.spans[0].text == 'description'
@@ -94,7 +94,7 @@ class ProgressLogEventGeneratorTest extends OutputSpecification {
         generator.onOutput(logEvent)
 
         then:
-        1 * target.onOutput({it instanceof StyledTextOutputEvent}) >> { args->
+        1 * target.onOutput({it instanceof StyledTextOutputEvent}) >> { args ->
             StyledTextOutputEvent event = args[0]
             assert event.spans.size() == 1
             assert event.spans[0].text == toNative('\n')
@@ -116,10 +116,10 @@ class ProgressLogEventGeneratorTest extends OutputSpecification {
         0 * target._
     }
 
-    def insertsLogMessagesWhenOperationGeneratesSomeOutputAndInDeferredHeaderMode() {
+    def insertsLogHeaderAndFooterWhenOperationGeneratesSomeOutputAndInDeferredHeaderMode() {
         ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, true)
         def logEvent = event('message')
-        def startEvent = start('description')
+        def startEvent = startWithHeader('description')
         def completeEvent = complete('status')
 
         when:
@@ -132,7 +132,7 @@ class ProgressLogEventGeneratorTest extends OutputSpecification {
         generator.onOutput(logEvent)
 
         then:
-        1 * target.onOutput({it instanceof StyledTextOutputEvent}) >> { args->
+        1 * target.onOutput({it instanceof StyledTextOutputEvent}) >> { args ->
             StyledTextOutputEvent event = args[0]
             assert event.spans.size() == 1
             assert event.spans[0].text == toNative('description\n')
@@ -159,10 +159,10 @@ class ProgressLogEventGeneratorTest extends OutputSpecification {
         ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, false)
 
         when:
-        generator.onOutput(start('description'))
+        generator.onOutput(startWithHeader('description'))
 
         then:
-        1 * target.onOutput(!null) >> { args->
+        1 * target.onOutput(!null) >> { args ->
             StyledTextOutputEvent event = args[0]
             assert event.spans.size() == 1
             assert event.spans[0].text == toNative('description')
@@ -170,15 +170,15 @@ class ProgressLogEventGeneratorTest extends OutputSpecification {
         0 * target._
 
         when:
-        generator.onOutput(start('description2'))
+        generator.onOutput(startWithHeader('description2'))
 
         then:
-        1 * target.onOutput({it instanceof StyledTextOutputEvent && it.spans[0].text == toNative('\n')}) >> { args->
+        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->
+        1 * target.onOutput(!null) >> { args ->
             StyledTextOutputEvent event = args[0]
             assert event.spans.size() == 1
             assert event.spans[0].text == toNative('description2')
@@ -216,8 +216,8 @@ class ProgressLogEventGeneratorTest extends OutputSpecification {
         ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, true)
 
         when:
-        generator.onOutput(start('description'))
-        generator.onOutput(start('description2'))
+        generator.onOutput(startWithHeader('description'))
+        generator.onOutput(startWithHeader('description2'))
 
         then:
         0 * target._
@@ -250,68 +250,76 @@ class ProgressLogEventGeneratorTest extends OutputSpecification {
         0 * target._
     }
 
-    def insertsLogMessagesWhenOperationHasNoFinalStatus() {
-        ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, false)
+    def insertsLogHeaderForOperationWhoseLoggingHeaderIsNotTheSameAsShortDescriptionWhenDeferredMode() {
+        ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, true)
+        def startEvent = start(loggingHeader: 'header', shortDescription: 'short-description')
+        def completeEvent = complete('status')
 
         when:
-        generator.onOutput(start('description'))
+        generator.onOutput(startEvent)
 
         then:
-        1 * target.onOutput({it instanceof StyledTextOutputEvent && it.spans[0].text == 'description'})
+        1 * target.onOutput(!null) >> { args ->
+            StyledTextOutputEvent event = args[0]
+            assert event.spans[0].text == 'header'
+        }
         0 * target._
 
         when:
-        generator.onOutput(complete(''))
+        generator.onOutput(completeEvent)
 
         then:
-        1 * target.onOutput({it instanceof StyledTextOutputEvent && it.spans[0].text == toNative('\n')})
+        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 insertsLogMessagesWhenOperationHasNoFinalStatusAndInDeferredMode() {
-        ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, true)
+    def insertsLogHeaderWhenOperationHasNoFinalStatus() {
+        ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, false)
 
         when:
-        generator.onOutput(start('description'))
+        generator.onOutput(startWithHeader('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('description\n')})
+        1 * target.onOutput({it instanceof StyledTextOutputEvent && it.spans[0].text == toNative('\n')})
         0 * target._
     }
 
-    def insertsLogMessagesWhenOperationHasNoDescription() {
-        ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, false)
+    def insertsLogHeaderWhenOperationHasNoFinalStatusAndInDeferredMode() {
+        ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, true)
 
         when:
-        generator.onOutput(start(''))
+        generator.onOutput(startWithHeader('description'))
 
         then:
         0 * target._
 
         when:
-        generator.onOutput(complete('status'))
+        generator.onOutput(complete(''))
 
         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')
-        }
+        1 * target.onOutput({it instanceof StyledTextOutputEvent && it.spans[0].text == toNative('description\n')})
         0 * target._
     }
 
-    def insertsLogMessagesWhenOperationHasNoDescriptionWhenInDeferredMode() {
-        ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, true)
+    def ignoresOperationsWithNoLogHeaderDefined() {
+        ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, false)
 
         when:
-        generator.onOutput(start(''))
+        generator.onOutput(startWithHeader(''))
 
         then:
         0 * target._
@@ -320,37 +328,31 @@ class ProgressLogEventGeneratorTest extends OutputSpecification {
         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)
+    def ignoresOperationsWithNoLogHeaderDefinedWhenInDeferredMode() {
+        ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, true)
 
         when:
-        generator.onOutput(start(''))
+        generator.onOutput(startWithHeader(''))
 
         then:
         0 * target._
 
         when:
-        generator.onOutput(complete(''))
+        generator.onOutput(complete('status'))
 
         then:
         0 * target._
     }
 
-    def insertsLogMessagesWhenOperationHasNoFinalStatusAndGeneratesLogMessages() {
+    def insertsLogHeaderWhenOperationHasNoFinalStatusAndGeneratesLogMessages() {
         ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, false)
         def event = event('event')
 
         when:
-        generator.onOutput(start('description'))
+        generator.onOutput(startWithHeader('description'))
         generator.onOutput(event)
 
         then:
@@ -366,12 +368,12 @@ class ProgressLogEventGeneratorTest extends OutputSpecification {
         0 * target._
     }
 
-    def insertsLogMessagesWhenOperationHasNoFinalStatusAndGeneratesLogMessagesAndInDeferredMode() {
+    def insertsLogHeaderWhenOperationHasNoFinalStatusAndGeneratesLogMessagesAndInDeferredMode() {
         ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, true)
         def event = event('event')
 
         when:
-        generator.onOutput(start('description'))
+        generator.onOutput(startWithHeader('description'))
         generator.onOutput(event)
 
         then:
@@ -390,8 +392,8 @@ class ProgressLogEventGeneratorTest extends OutputSpecification {
         ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, true)
 
         when:
-        generator.onOutput(start(''))
-        generator.onOutput(start(''))
+        generator.onOutput(startWithHeader(''))
+        generator.onOutput(startWithHeader('nested'))
 
         then:
         0 * target._
@@ -402,9 +404,10 @@ class ProgressLogEventGeneratorTest extends OutputSpecification {
         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')
+            assert event.spans.size() == 3
+            assert event.spans[0].text == toNative('nested ')
+            assert event.spans[1].text == toNative('status2')
+            assert event.spans[2].text == toNative('\n')
         }
         0 * target._
 
@@ -412,20 +415,14 @@ class ProgressLogEventGeneratorTest extends OutputSpecification {
         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(startWithHeader('description'))
         generator.onOutput(progress('tick'))
         generator.onOutput(complete('status'))
 
@@ -440,4 +437,8 @@ class ProgressLogEventGeneratorTest extends OutputSpecification {
         }
         0 * target._
     }
+
+    def startWithHeader(String header) {
+        return start(loggingHeader: header, shortDescription: header)
+    }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/internal/StreamBackedStandardOutputListenerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/internal/StreamBackedStandardOutputListenerTest.groovy
new file mode 100644
index 0000000..e213d5b
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/internal/StreamBackedStandardOutputListenerTest.groovy
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+class StreamBackedStandardOutputListenerTest extends Specification {
+    def canAppendToAnAppendable() {
+        Appendable appendable = Mock()
+        def listener = new StreamBackedStandardOutputListener(appendable)
+
+        when:
+        listener.onOutput("text")
+
+        then:
+        1 * appendable.append("text")
+        0 * _._
+    }
+
+    def canAppendToAWriter() {
+        Writer writer = Mock()
+        def listener = new StreamBackedStandardOutputListener(writer)
+
+        when:
+        listener.onOutput("text")
+
+        then:
+        1 * writer.append("text")
+        1 * writer.flush()
+        0 * _._
+    }
+    
+    def canAppendToAnOutputStream() {
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream()
+        def listener = new StreamBackedStandardOutputListener(outputStream)
+
+        when:
+        listener.onOutput("text")
+
+        then:
+        outputStream.toString() == "text"
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/process/internal/DefaultExecHandleTest.java b/subprojects/core/src/test/groovy/org/gradle/process/internal/DefaultExecHandleTest.java
index e756215..89b00d5 100644
--- a/subprojects/core/src/test/groovy/org/gradle/process/internal/DefaultExecHandleTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/process/internal/DefaultExecHandleTest.java
@@ -87,7 +87,7 @@ public class DefaultExecHandleTest {
             result.assertNormalExitValue();
             fail();
         } catch (ExecException e) {
-            assertEquals("Display-name finished with non-zero exit value.", e.getMessage());
+            assertEquals("Display-name finished with (non-zero) exit value 72.", e.getMessage());
         }
     }
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/process/internal/DefaultWorkerProcessFactoryTest.java b/subprojects/core/src/test/groovy/org/gradle/process/internal/DefaultWorkerProcessFactoryTest.java
index e36da5f..10aeecd 100644
--- a/subprojects/core/src/test/groovy/org/gradle/process/internal/DefaultWorkerProcessFactoryTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/process/internal/DefaultWorkerProcessFactoryTest.java
@@ -19,11 +19,13 @@ package org.gradle.process.internal;
 import org.gradle.api.Action;
 import org.gradle.api.internal.ClassPathRegistry;
 import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.file.collections.SimpleFileCollection;
 import org.gradle.api.logging.LogLevel;
 import org.gradle.messaging.remote.MessagingServer;
 import org.gradle.process.internal.child.IsolatedApplicationClassLoaderWorker;
 import org.gradle.process.internal.launcher.GradleWorkerMain;
 import org.gradle.util.IdGenerator;
+import org.hamcrest.Matchers;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.integration.junit4.JUnit4Mockery;
@@ -58,7 +60,9 @@ public class DefaultWorkerProcessFactoryTest {
         context.checking(new Expectations() {{
             one(classPathRegistry).getClassPathFiles("WORKER_PROCESS");
             will(returnValue(processClassPath));
-            ignoring(fileResolver);
+            allowing(fileResolver).resolveLater(".");
+            allowing(fileResolver).resolveFiles(with(Matchers.<Object>notNullValue()));
+            will(returnValue(new SimpleFileCollection()));
         }});
 
         WorkerProcessBuilder builder = factory.create();
diff --git a/subprojects/core/src/test/groovy/org/gradle/process/internal/DefaultWorkerProcessTest.groovy b/subprojects/core/src/test/groovy/org/gradle/process/internal/DefaultWorkerProcessTest.groovy
index dd93c7e..9a192ec 100644
--- a/subprojects/core/src/test/groovy/org/gradle/process/internal/DefaultWorkerProcessTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/process/internal/DefaultWorkerProcessTest.groovy
@@ -109,7 +109,7 @@ class DefaultWorkerProcessTest extends MultithreadedTestCase {
     @Test
     public void startThrowsExceptionOnChildProcessFailure() {
         def listener = expectAttachesListener()
-        def failure = new ExecException('broken')
+        def failure = new RuntimeException('broken')
         ExecResult execResult = context.mock(ExecResult.class)
 
         context.checking {
@@ -129,7 +129,7 @@ class DefaultWorkerProcessTest extends MultithreadedTestCase {
             try {
                 workerProcess.start()
                 fail()
-            } catch (ExecException e) {
+            } catch (RuntimeException e) {
                 assertThat(e, sameInstance(failure))
             }
         }
diff --git a/subprojects/core/src/test/groovy/org/gradle/process/internal/JavaExecHandleBuilderTest.groovy b/subprojects/core/src/test/groovy/org/gradle/process/internal/JavaExecHandleBuilderTest.groovy
index b42d98a..f972cb0 100644
--- a/subprojects/core/src/test/groovy/org/gradle/process/internal/JavaExecHandleBuilderTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/process/internal/JavaExecHandleBuilderTest.groovy
@@ -19,7 +19,7 @@ package org.gradle.process.internal;
 import org.gradle.api.file.FileCollection
 import org.gradle.api.internal.file.FileResolver
 import org.gradle.api.internal.file.IdentityFileResolver
-import org.gradle.api.internal.file.PathResolvingFileCollection
+import org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection
 import org.gradle.util.Jvm
 import spock.lang.Specification
 import static java.util.Arrays.asList
@@ -40,7 +40,7 @@ public class JavaExecHandleBuilderTest extends Specification {
         File jar1 = new File("file1.jar").canonicalFile
         File jar2 = new File("file2.jar").canonicalFile
 
-        FileCollection classpath = new PathResolvingFileCollection(fileResolver, null, jar1, jar2)
+        FileCollection classpath = new DefaultConfigurableFileCollection(fileResolver, null, jar1, jar2)
 
         builder.main = 'mainClass'
         builder.args("arg1", "arg2")
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/ConcurrentSpecification.groovy b/subprojects/core/src/test/groovy/org/gradle/util/ConcurrentSpecification.groovy
index 0c0d01a..660aead 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/ConcurrentSpecification.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/util/ConcurrentSpecification.groovy
@@ -19,6 +19,8 @@ import java.util.concurrent.TimeUnit
 import java.util.concurrent.locks.Condition
 import java.util.concurrent.locks.Lock
 import java.util.concurrent.locks.ReentrantLock
+import org.gradle.messaging.concurrent.ExecutorFactory
+import org.gradle.messaging.concurrent.StoppableExecutor
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 import spock.lang.Specification
@@ -40,6 +42,24 @@ class ConcurrentSpecification extends Specification {
         finished()
     }
 
+    ExecutorFactory getExecutorFactory() {
+        return new ExecutorFactory() {
+            StoppableExecutor create(String displayName) {
+                return new StoppableExecutorStub(ConcurrentSpecification.this)
+            }
+        }
+    }
+
+    void startThread(Runnable cl) {
+        lock.lock()
+        try {
+            TestThread thread = new TestThread(this, lock, cl)
+            thread.start()
+        } finally {
+            lock.unlock()
+        }
+    }
+
     /**
      * Creates an action which will be used by a mock object to synchronise with the SUT.
      *
@@ -57,11 +77,11 @@ class ConcurrentSpecification extends Specification {
     }
 
     /**
-     * Starts a thread which executes the given closure. Blocks until all deferred actions are activated. Does not wait for the thread to complete.
+     * Starts a thread which executes the given action/closure. Blocks until all deferred actions are activated. Does not wait for the thread to complete.
      *
      * @return A handle to the test thread.
      */
-    TestParticipant start(Closure cl) {
+    TestParticipant start(Runnable cl) {
         lock.lock()
         try {
             TestThread thread = new TestThread(this, lock, cl)
@@ -258,7 +278,7 @@ interface LongRunningAction {
 }
 
 /**
- * An action which runs at some point in the future. A {@code DeferredAction} must be activated before it can run, by calling {@link DeferredAction#activate(Closure)}.
+ * An action which runs at some point in the future. A {@code DeferredAction} must be activated before it can run, by calling {@link DeferredAction#finishLater(Closure)}.
  */
 interface DeferredAction extends LongRunningAction {
     /**
@@ -272,7 +292,20 @@ interface DeferredAction extends LongRunningAction {
      * <li>{@link ConcurrentSpecification#finished}</li>
      * </ul>
      */
-    void activate(Closure action)
+    void finishLater(Closure action)
+
+    /**
+     * Registers that the target sync point has been reached, and this action is ready to execute. This method blocks until the action completes.
+     *
+     * This action is started once this method has been called, and one of the following have been executed:
+     *
+     * <ul>
+     * <li>{@link TestParticipant#waitsFor(LongRunningAction)}</li>
+     * <li>{@link TestParticipant#doesNotWaitFor(LongRunningAction)}</li>
+     * <li>{@link ConcurrentSpecification#finished}</li>
+     * </ul>
+     */
+    void finish()
 }
 
 interface TestParticipant extends LongRunningAction {
@@ -341,6 +374,17 @@ class DeferredActionImpl extends AbstractAction implements DeferredAction {
         }
     }
 
+    void running() {
+        lock.lock()
+        try {
+            if (complete) {
+                throw new IllegalStateException("Action has completed.")
+            }
+        } finally {
+            lock.unlock()
+        }
+    }
+
     @Override
     void completesBefore(Date timeout) {
         activated()
@@ -384,7 +428,7 @@ class DeferredActionImpl extends AbstractAction implements DeferredAction {
         }
     }
 
-    void activate(Closure action) {
+    void finishLater(Closure action) {
         lock.lock()
         try {
             if (activated) {
@@ -398,6 +442,11 @@ class DeferredActionImpl extends AbstractAction implements DeferredAction {
             lock.unlock()
         }
     }
+
+    void finish() {
+        finishLater {}
+        completed()
+    }
 }
 
 abstract class AbstractTestParticipant extends AbstractAction implements TestParticipant {
@@ -410,6 +459,7 @@ abstract class AbstractTestParticipant extends AbstractAction implements TestPar
     void doesNotWaitFor(LongRunningAction... targets) {
         targets*.activated()
         completed()
+        targets*.running()
     }
 
     void waitsFor(LongRunningAction... targets) {
@@ -475,3 +525,27 @@ class CompositeTestParticipant extends AbstractTestParticipant {
         }
     }
 }
+
+class StoppableExecutorStub implements StoppableExecutor {
+    final ConcurrentSpecification owner
+
+    StoppableExecutorStub(ConcurrentSpecification owner) {
+        this.owner = owner
+    }
+
+    void stop() {
+        throw new UnsupportedOperationException()
+    }
+
+    void stop(int timeoutValue, TimeUnit timeoutUnits) {
+        throw new UnsupportedOperationException()
+    }
+
+    void requestStop() {
+        throw new UnsupportedOperationException()
+    }
+
+    void execute(Runnable runnable) {
+        owner.startThread(runnable)
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/ConcurrentSpecificationTest.groovy b/subprojects/core/src/test/groovy/org/gradle/util/ConcurrentSpecificationTest.groovy
index 44feb4e..38ebdb8 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/ConcurrentSpecificationTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/util/ConcurrentSpecificationTest.groovy
@@ -21,7 +21,8 @@ import java.util.concurrent.TimeUnit
 import spock.lang.Ignore
 
 class ConcurrentSpecificationTest extends ConcurrentSpecification {
-    def canCheckThatMethodCallWaitsUntilAsyncActionIsComplete() {
+    
+    def canCheckThatMethodCallWaitsUntilAsyncCallbackIsComplete() {
         SomeAsyncWorker worker = Mock()
         SomeSyncClass target = new SomeSyncClass(worker: worker)
 
@@ -35,7 +36,7 @@ class ConcurrentSpecificationTest extends ConcurrentSpecification {
 
         then:
         action.waitsFor(notifyListener)
-        1 * worker.doLater(!null) >> { args -> notifyListener.activate { args[0].call('result') } }
+        1 * worker.doLater(!null) >> { args -> notifyListener.finishLater { args[0].call('result') } }
 
         when:
         finished()
@@ -44,7 +45,7 @@ class ConcurrentSpecificationTest extends ConcurrentSpecification {
         result == 'result'
     }
 
-    def canCheckThatMethodCallDoesNotWaitUntilAsyncActionIsComplete() {
+    def canCheckThatMethodCallDoesNotWaitUntilAsyncCallbackIsComplete() {
         SomeAsyncWorker worker = Mock()
         Closure handler = Mock()
         SomeSyncClass target = new SomeSyncClass(worker: worker)
@@ -58,7 +59,7 @@ class ConcurrentSpecificationTest extends ConcurrentSpecification {
 
         then:
         action.doesNotWaitFor(notifyListener)
-        1 * worker.doLater(!null) >> { args -> notifyListener.activate { args[0].call('result') } }
+        1 * worker.doLater(!null) >> { args -> notifyListener.finishLater { args[0].call('result') } }
 
         when:
         finished()
@@ -87,9 +88,9 @@ class ConcurrentSpecificationTest extends ConcurrentSpecification {
         2 * worker.doLater(!null) >> { args ->
             if (!listener) {
                 listener = args[0];
-                notifyListener.activate { args[0].call('result1') }
+                notifyListener.finishLater { args[0].call('result1') }
             } else {
-                notifyListenerAgain.activate { args[0].call('result2') }
+                notifyListenerAgain.finishLater { args[0].call('result2') }
             }
         }
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/GUtilTest.java b/subprojects/core/src/test/groovy/org/gradle/util/GUtilTest.java
index a5a89a4..53b8507 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/GUtilTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/util/GUtilTest.java
@@ -42,6 +42,32 @@ public class GUtilTest {
     }
 
     @Test
+    public void convertStringToConstantName() {
+        assertThat(toConstant(null), equalTo(null));
+        assertThat(toConstant(""), equalTo(""));
+        assertThat(toConstant("word"), equalTo("WORD"));
+        assertThat(toConstant("twoWords"), equalTo("TWO_WORDS"));
+        assertThat(toConstant("TwoWords"), equalTo("TWO_WORDS"));
+        assertThat(toConstant("two-words"), equalTo("TWO_WORDS"));
+        assertThat(toConstant("TWO_WORDS"), equalTo("TWO_WORDS"));
+        assertThat(toConstant("two.words"), equalTo("TWO_WORDS"));
+        assertThat(toConstant("two words"), equalTo("TWO_WORDS"));
+        assertThat(toConstant("two Words"), equalTo("TWO_WORDS"));
+        assertThat(toConstant("Two Words"), equalTo("TWO_WORDS"));
+        assertThat(toConstant(" Two  \t words\n"), equalTo("TWO_WORDS"));
+        assertThat(toConstant("four or so Words"), equalTo("FOUR_OR_SO_WORDS"));
+        assertThat(toConstant("trailing-"), equalTo("TRAILING"));
+        assertThat(toConstant("a"), equalTo("A"));
+        assertThat(toConstant("A"), equalTo("A"));
+        assertThat(toConstant("aB"), equalTo("A_B"));
+        assertThat(toConstant("ABC"), equalTo("ABC"));
+        assertThat(toConstant("ABCThing"), equalTo("ABC_THING"));
+        assertThat(toConstant("ABC Thing"), equalTo("ABC_THING"));
+        assertThat(toConstant("."), equalTo(""));
+        assertThat(toConstant("-"), equalTo(""));
+    }
+
+    @Test
     public void convertStringToWords() {
         assertThat(toWords(null), equalTo(null));
         assertThat(toWords(""), equalTo(""));
@@ -55,7 +81,11 @@ public class GUtilTest {
         assertThat(toWords("two.words"), equalTo("two words"));
         assertThat(toWords("two,words"), equalTo("two words"));
         assertThat(toWords("trailing-"), equalTo("trailing"));
-        assertThat(toWords("ABC"), equalTo("a b c"));
+        assertThat(toWords("a"), equalTo("a"));
+        assertThat(toWords("aB"), equalTo("a b"));
+        assertThat(toWords("ABC"), equalTo("abc"));
+        assertThat(toWords("ABCThing"), equalTo("abc thing"));
+        assertThat(toWords("ABC Thing"), equalTo("abc thing"));
         assertThat(toWords("."), equalTo(""));
         assertThat(toWords("_"), equalTo(""));
     }
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/GradleVersionTest.groovy b/subprojects/core/src/test/groovy/org/gradle/util/GradleVersionTest.groovy
index 8b90542..5272d38 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/GradleVersionTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/util/GradleVersionTest.groovy
@@ -16,38 +16,53 @@
 
 package org.gradle.util
 
-import org.codehaus.groovy.runtime.InvokerHelper
-import static org.junit.Assert.*
-
-import org.apache.tools.ant.Main
 import org.apache.ivy.Ivy
-import spock.lang.Specification;
+import org.apache.tools.ant.Main
+import org.codehaus.groovy.runtime.InvokerHelper
+import spock.lang.Specification
 
 /**
  * @author Hans Dockter
  */
 class GradleVersionTest extends Specification {
-    final GradleVersion version = new GradleVersion()
+    final GradleVersion version = GradleVersion.current()
 
     def equalsAndHashCode() {
         expect:
-        Matchers.strictlyEquals(new GradleVersion('0.9'), new GradleVersion('0.9'))
-        new GradleVersion('0.9') != new GradleVersion('1.0')
+        Matchers.strictlyEquals(GradleVersion.version('0.9'), GradleVersion.version('0.9'))
+        GradleVersion.version('0.9') != GradleVersion.version('1.0')
+    }
+
+    def canConstructPatchVersion() {
+        when:
+        def version = GradleVersion.version('1.0-milestone-2')
+        def patch = GradleVersion.version('1.0-milestone-2a')
+        def nextPatch = GradleVersion.version('1.0-milestone-2b')
+        def nextMilestone = GradleVersion.version('1.0-milestone-3')
+
+        then:
+        version < patch
+        patch < nextPatch
+        nextPatch < nextMilestone
+
+        patch > version
+        nextPatch > patch
+        nextMilestone > nextPatch
     }
 
     def canConstructSnapshotVersion() {
         expect:
-        new GradleVersion('0.9-20101220110000+1100').snapshot
-        new GradleVersion('0.9-20101220110000-0800').snapshot
-        !new GradleVersion('0.9-rc-1').snapshot
+        GradleVersion.version('0.9-20101220110000+1100').snapshot
+        GradleVersion.version('0.9-20101220110000-0800').snapshot
+        !GradleVersion.version('0.9-rc-1').snapshot
     }
 
     def canCompareMajorVersions() {
         expect:
-        new GradleVersion(a) > new GradleVersion(b)
-        new GradleVersion(b) < new GradleVersion(a)
-        new GradleVersion(a) == new GradleVersion(a)
-        new GradleVersion(b) == new GradleVersion(b)
+        GradleVersion.version(a) > GradleVersion.version(b)
+        GradleVersion.version(b) < GradleVersion.version(a)
+        GradleVersion.version(a) == GradleVersion.version(a)
+        GradleVersion.version(b) == GradleVersion.version(b)
 
         where:
         a | b
@@ -59,10 +74,10 @@ class GradleVersionTest extends Specification {
 
     def canComparePointVersions() {
         expect:
-        new GradleVersion(a) > new GradleVersion(b)
-        new GradleVersion(b) < new GradleVersion(a)
-        new GradleVersion(a) == new GradleVersion(a)
-        new GradleVersion(b) == new GradleVersion(b)
+        GradleVersion.version(a) > GradleVersion.version(b)
+        GradleVersion.version(b) < GradleVersion.version(a)
+        GradleVersion.version(a) == GradleVersion.version(a)
+        GradleVersion.version(b) == GradleVersion.version(b)
 
         where:
         a | b
@@ -72,10 +87,10 @@ class GradleVersionTest extends Specification {
 
     def canComparePointVersionAndMajorVersions() {
         expect:
-        new GradleVersion(a) > new GradleVersion(b)
-        new GradleVersion(b) < new GradleVersion(a)
-        new GradleVersion(a) == new GradleVersion(a)
-        new GradleVersion(b) == new GradleVersion(b)
+        GradleVersion.version(a) > GradleVersion.version(b)
+        GradleVersion.version(b) < GradleVersion.version(a)
+        GradleVersion.version(a) == GradleVersion.version(a)
+        GradleVersion.version(b) == GradleVersion.version(b)
 
         where:
         a | b
@@ -85,10 +100,10 @@ class GradleVersionTest extends Specification {
 
     def canComparePreviewsMilestonesAndRCVersions() {
         expect:
-        new GradleVersion(a) > new GradleVersion(b)
-        new GradleVersion(b) < new GradleVersion(a)
-        new GradleVersion(a) == new GradleVersion(a)
-        new GradleVersion(b) == new GradleVersion(b)
+        GradleVersion.version(a) > GradleVersion.version(b)
+        GradleVersion.version(b) < GradleVersion.version(a)
+        GradleVersion.version(a) == GradleVersion.version(a)
+        GradleVersion.version(b) == GradleVersion.version(b)
 
         where:
         a | b
@@ -102,10 +117,10 @@ class GradleVersionTest extends Specification {
 
     def canCompareSnapshotVersions() {
         expect:
-        new GradleVersion(a) > new GradleVersion(b)
-        new GradleVersion(b) < new GradleVersion(a)
-        new GradleVersion(a) == new GradleVersion(a)
-        new GradleVersion(b) == new GradleVersion(b)
+        GradleVersion.version(a) > GradleVersion.version(b)
+        GradleVersion.version(b) < GradleVersion.version(a)
+        GradleVersion.version(a) == GradleVersion.version(a)
+        GradleVersion.version(b) == GradleVersion.version(b)
 
         where:
         a | b
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/LineBufferingOutputStreamTest.java b/subprojects/core/src/test/groovy/org/gradle/util/LineBufferingOutputStreamTest.java
index db381aa..0a69ded 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/LineBufferingOutputStreamTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/util/LineBufferingOutputStreamTest.java
@@ -35,7 +35,7 @@ public class LineBufferingOutputStreamTest {
 
     @Before
     public void setUp() {
-        eol = System.getProperty("line.separator");
+        eol = SystemProperties.getLineSeparator();
     }
 
     @After
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/Matchers.java b/subprojects/core/src/test/groovy/org/gradle/util/Matchers.java
index c8a78fc..6230a50 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/Matchers.java
+++ b/subprojects/core/src/test/groovy/org/gradle/util/Matchers.java
@@ -18,6 +18,9 @@ package org.gradle.util;
 
 import org.gradle.api.Buildable;
 import org.gradle.api.Task;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.UnionFileCollection;
+import org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection;
 import org.hamcrest.*;
 import org.jmock.api.Action;
 import org.jmock.api.Invocation;
@@ -29,8 +32,8 @@ import java.io.StringReader;
 import java.util.*;
 import java.util.regex.Pattern;
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertTrue;
 
 public class Matchers {
     @Factory
@@ -75,6 +78,19 @@ public class Matchers {
     }
 
     @Factory
+    public static <T extends CharSequence> Matcher<T> matchesRegexp(final Pattern pattern) {
+        return new BaseMatcher<T>() {
+            public boolean matches(Object o) {
+                return pattern.matcher((CharSequence) o).matches();
+            }
+
+            public void describeTo(Description description) {
+                description.appendText("a CharSequence that matches regexp ").appendValue(pattern);
+            }
+        };
+    }
+
+    @Factory
     public static <T> Matcher<T> strictlyEqual(final T other) {
         return new BaseMatcher<T>() {
             public boolean matches(Object o) {
@@ -109,6 +125,7 @@ public class Matchers {
         return true;
 
     }
+
     @Factory
     public static Matcher<String> containsLine(final String line) {
         return new BaseMatcher<String>() {
@@ -139,7 +156,7 @@ public class Matchers {
     public static boolean containsLine(String action, String expected) {
         return containsLine(action, equalTo(expected));
     }
-    
+
     public static boolean containsLine(String actual, Matcher<? super String> matcher) {
         BufferedReader reader = new BufferedReader(new StringReader(actual));
         String line;
@@ -215,7 +232,7 @@ public class Matchers {
     public static Matcher<Task> dependsOn(final String... tasks) {
         return dependsOn(equalTo(new HashSet<String>(Arrays.asList(tasks))));
     }
-    
+
     @Factory
     public static Matcher<Task> dependsOn(final Matcher<? extends Iterable<String>> matcher) {
         return new BaseMatcher<Task>() {
@@ -271,6 +288,39 @@ public class Matchers {
         };
     }
 
+    @Factory
+    public static <T extends FileCollection> Matcher<T> sameCollection(final FileCollection expected) {
+        return new BaseMatcher<T>() {
+            public boolean matches(Object o) {
+                FileCollection actual = (FileCollection) o;
+                List<? extends FileCollection> actualCollections = unpack(actual);
+                List<? extends FileCollection> expectedCollections = unpack(expected);
+                boolean equals = actualCollections.equals(expectedCollections);
+                if (!equals) {
+                    System.out.println("expected: " + expectedCollections);
+                    System.out.println("actual: " + actualCollections);
+                }
+                return equals;
+            }
+
+            private List<? extends FileCollection> unpack(FileCollection expected) {
+                if (expected instanceof UnionFileCollection) {
+                    UnionFileCollection collection = (UnionFileCollection) expected;
+                    return new ArrayList<FileCollection>(collection.getSources());
+                }
+                if (expected instanceof DefaultConfigurableFileCollection) {
+                    DefaultConfigurableFileCollection collection = (DefaultConfigurableFileCollection) expected;
+                    return new ArrayList<FileCollection>((Set) collection.getFrom());
+                }
+                throw new RuntimeException("Cannot get children of " + expected);
+            }
+
+            public void describeTo(Description description) {
+                description.appendText("same file collection as ").appendValue(expected);
+            }
+        };
+    }
+
     /**
      * Returns a placeholder for a mock method parameter.
      */
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/NameMatcherTest.java b/subprojects/core/src/test/groovy/org/gradle/util/NameMatcherTest.java
index 65bd007..4659a21 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/NameMatcherTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/util/NameMatcherTest.java
@@ -66,9 +66,24 @@ public class NameMatcherTest {
     }
 
     @Test
-    public void prefersExactMatchOverInexactMatch() {
+    public void prefersExactMatchOverCaseInsensitiveMatch() {
         assertMatches("name", "name", "Name", "NAME");
-        assertMatches("name", "name", "names");
+        assertMatches("someName", "someName", "SomeName", "somename", "SOMENAME");
+        assertMatches("some Name", "some Name", "Some Name", "some name", "SOME NAME");
+    }
+
+    @Test
+    public void prefersExactMatchOverPartialMatch() {
+        assertMatches("name", "name", "nam", "n", "NAM");
+    }
+
+    @Test
+    public void prefersExactMatchOverPrefixMatch() {
+        assertMatches("someName", "someName", "someNameWithExtra");
+    }
+
+    @Test
+    public void prefersExactMatchOverCamelCaseMatch() {
         assertMatches("sName", "sName", "someName", "sNames");
         assertMatches("so Name", "so Name", "some Name", "so name");
         assertMatches("ABC", "ABC", "AaBbCc");
@@ -82,10 +97,16 @@ public class NameMatcherTest {
     }
 
     @Test
-    public void prefersCaseSensitiveMatchOverCaseInsensitiveMatch() {
-        assertMatches("sN", "someName", "sn");
+    public void prefersCaseSensitiveCamelCaseMatchOverCaseInsensitiveCamelCaseMatch() {
+        assertMatches("soNa", "someName", "somename");
         assertMatches("SN", "SomeName", "someName");
-        assertMatches("n1", "name1", "Name1", "N1");
+        assertMatches("na1", "name1", "Name1", "NAME1");
+    }
+
+    @Test
+    public void prefersCaseInsensitiveMatchOverCamelCaseMatch() {
+        assertMatches("somename", "someName", "someNameWithExtra");
+        assertMatches("soNa", "sona", "someName");
     }
 
     @Test
@@ -100,12 +121,18 @@ public class NameMatcherTest {
     }
 
     @Test
-    public void doesNotSelectItemsWhenMultipleMatches() {
+    public void doesNotSelectItemsWhenMultipleCamelCaseMatches() {
         assertThat(matcher.find("sN", toList("someName", "soNa", "other")), nullValue());
         assertThat(matcher.getMatches(), equalTo(toSet("someName", "soNa")));
     }
 
     @Test
+    public void doesNotSelectItemsWhenMultipleCaseInsensitiveMatches() {
+        assertThat(matcher.find("someName", toList("somename", "SomeName", "other")), nullValue());
+        assertThat(matcher.getMatches(), equalTo(toSet("somename", "SomeName")));
+    }
+
+    @Test
     public void emptyPatternDoesNotSelectAnything() {
         assertDoesNotMatch("", "something");
     }
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/StageTest.groovy b/subprojects/core/src/test/groovy/org/gradle/util/StageTest.groovy
new file mode 100644
index 0000000..e501cdb
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/util/StageTest.groovy
@@ -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.util
+
+import org.gradle.util.GradleVersion.Stage
+import spock.lang.Specification
+
+/**
+ * @author Szczepan Faber
+ */
+class StageTest extends Specification {
+    def "builds simple stage"() {
+        when:
+        def s = new Stage(3, "4")
+
+        then:
+        s.stage == 3
+        s.number == 4
+    }
+
+    def "builds stage with patch no"() {
+        when:
+        def s = new Stage(3, "4a")
+
+        then:
+        s.stage == 3
+        s.number == 4
+        s.patchNo == 'a'
+    }
+
+    def "compares correctly with patch no"() {
+        expect:
+        new Stage(1, "2") < new Stage(1, "3")
+        new Stage(1, "4") > new Stage(1, "3")
+
+        new Stage(1, "9") < new Stage(2, "1")
+        new Stage(2, "1") > new Stage(1, "9")
+
+        new Stage(1, "1") < new Stage(1, "1a")
+        new Stage(1, "1a") > new Stage(1, "1")
+
+        new Stage(1, "1a") < new Stage(1, "1b")
+        new Stage(1, "1b") > new Stage(1, "1a")
+
+        new Stage(1, "1c") < new Stage(1, "2b")
+        new Stage(1, "2b") > new Stage(1, "1c")
+    }
+
+    def "shows input when matching fails"() {
+        when:
+        new Stage(1, "x")
+
+        then:
+        def ex = thrown(Exception)
+        ex.message.contains("x")
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/TestFile.java b/subprojects/core/src/test/groovy/org/gradle/util/TestFile.java
index 8f67505..54d6e28 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/TestFile.java
+++ b/subprojects/core/src/test/groovy/org/gradle/util/TestFile.java
@@ -81,7 +81,11 @@ public class TestFile extends File implements TestFileContext {
     }
 
     public TestFile file(Object... path) {
-        return new TestFile(this, path);
+        try {
+            return new TestFile(this, path);
+        } catch (RuntimeException e) {
+            throw new UncheckedIOException(String.format("Could not locate file '%s' relative to '%s'.", Arrays.toString(path), this), e);
+        }
     }
 
     public List<TestFile> files(Object... paths) {
@@ -251,7 +255,7 @@ public class TestFile extends File implements TestFileContext {
 
     @Override
     public TestFile getParentFile() {
-        return new TestFile(super.getParentFile());
+        return super.getParentFile() == null ? null : new TestFile(super.getParentFile());
     }
 
     @Override
@@ -295,6 +299,7 @@ public class TestFile extends File implements TestFileContext {
     public TestFile assertIsCopyOf(TestFile other) {
         assertIsFile();
         other.assertIsFile();
+        assertEquals(other.length(), this.length());
         assertTrue(Arrays.equals(HashUtil.createHash(this), HashUtil.createHash(other)));
         return this;
     }
@@ -336,6 +341,14 @@ public class TestFile extends File implements TestFileContext {
         return this;
     }
 
+    public TestFile assertIsEmptyDir() {
+        if (exists()) {
+            assertIsDir();
+            assertHasDescendants();
+        }
+        return this;
+    }
+
     private void visit(Set<String> names, String prefix, File file) {
         for (File child : file.listFiles()) {
             if (child.isFile()) {
@@ -358,6 +371,10 @@ public class TestFile extends File implements TestFileContext {
         return this;
     }
 
+    public TestFile createDir(Object path) {
+        return new TestFile(this, path).createDir();
+    }
+
     public TestFile deleteDir() {
         DeleteAction delete = new DeleteActionImpl(new IdentityFileResolver());
         delete.delete(this);
@@ -387,6 +404,10 @@ public class TestFile extends File implements TestFileContext {
         return this;
     }
 
+    public TestFile createFile(Object path) {
+        return file(path).createFile();
+    }
+
     public TestFile zipTo(TestFile zipFile) {
         Zip zip = new Zip();
         zip.setBasedir(this);
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/TextUtilTest.groovy b/subprojects/core/src/test/groovy/org/gradle/util/TextUtilTest.groovy
index e6734ee..80f30df 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/TextUtilTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/util/TextUtilTest.groovy
@@ -19,14 +19,24 @@ package org.gradle.util
 import spock.lang.Specification
 
 class TextUtilTest extends Specification {
-    private static String sep = TextUtil.LINE_SEPARATOR
+    private static String sep = "separator"
+    private static String platformSep = TextUtil.platformLineSeparator
 
-    def "toNativeLineSeparators() converts all line separators to native ones"() {
+    def convertLineSeparators() {
         expect:
-        TextUtil.toNativeLineSeparators(original) == converted
+        TextUtil.convertLineSeparators(original, sep) == converted
 
         where:
         original                          | converted
         "one\rtwo\nthree\r\nfour\n\rfive" | "one${sep}two${sep}three${sep}four${sep}${sep}five"
     }
+
+    def toPlatformLineSeparators() {
+        expect:
+        TextUtil.toPlatformLineSeparators(original) == converted
+
+        where:
+        original | converted
+        "one\rtwo\nthree\r\nfour\n\rfive" | "one${platformSep}two${platformSep}three${platformSep}four${platformSep}${platformSep}five"
+    }
 }
diff --git a/subprojects/core/src/test/resources/org/gradle/api/internal/tasks/generator/defaultResource.xml b/subprojects/core/src/test/resources/org/gradle/api/internal/tasks/generator/defaultResource.xml
deleted file mode 100644
index b562ead..0000000
--- a/subprojects/core/src/test/resources/org/gradle/api/internal/tasks/generator/defaultResource.xml
+++ /dev/null
@@ -1 +0,0 @@
-<default-xml/>
\ No newline at end of file
diff --git a/subprojects/docs/docs.gradle b/subprojects/docs/docs.gradle
index a4a86d9..4042c52 100644
--- a/subprojects/docs/docs.gradle
+++ b/subprojects/docs/docs.gradle
@@ -21,20 +21,14 @@ import org.gradle.build.docs.dsl.docbook.AssembleDslDocTask
 import org.gradle.build.docs.dsl.ExtractDslMetaDataTask
 
 apply plugin: 'base'
+apply from: "${rootDir}/gradle/ssh.gradle"
 
 configurations {
-    ftpAntTask
     userGuideStyleSheets
     userGuideTask
 }
 
 dependencies {
-    ftpAntTask module("org.apache.ant:ant-commons-net:1.7.0") {
-        module("commons-net:commons-net:1.4.1") {
-            dependencies("oro:oro:2.0.8 at jar")
-        }
-    }
-
     userGuideTask 'xalan:xalan:2.7.1', 'xerces:xercesImpl:2.9.1'
     userGuideTask module('xhtmlrenderer:xhtmlrenderer:R8rc1') {
         dependency 'itext:itext:2.0.8 at jar'
@@ -59,26 +53,26 @@ docbookSrc = new File(project.buildDir, 'src')
 samplesSrcDir = file('src/samples')
 websiteDocs = new File(buildDir, 'websiteDocs')
 
-tasks.withType(Docbook2Xhtml).allObjects { task->
-    task.dependsOn userguideStyleSheets
-    task.classpath = configurations.userGuideTask
-    task.stylesheetsDir = userguideStyleSheets.destinationDir
+tasks.withType(Docbook2Xhtml) {
+    dependsOn userguideStyleSheets
+    classpath = configurations.userGuideTask
+    stylesheetsDir = userguideStyleSheets.destinationDir
 }
-tasks.withType(UserGuideTransformTask).allObjects { task->
-    task.classpath = configurations.userGuideTask
-    task.dependsOn samples, dslDocbook
-    task.snippetsDir = samples.snippetsDir
-    task.linksFile = dslDocbook.linksFile
+tasks.withType(UserGuideTransformTask) {
+    classpath = configurations.userGuideTask
+    dependsOn samples, dslDocbook
+    snippetsDir = samples.snippetsDir
+    linksFile = dslDocbook.linksFile
 }
-tasks.withType(AssembleDslDocTask).allObjects { task ->
-    task.classpath = configurations.userGuideTask
-    task.classDocbookDir = dslSrcDir
+tasks.withType(AssembleDslDocTask) {
+    classpath = configurations.userGuideTask
+    classDocbookDir = dslSrcDir
 }
 
 task samples(type: ExtractSnippetsTask) {
     source samplesSrcDir
     exclude 'userguideOutput/**'
-    exclude 'userguide/tutorial/antChecksumFiles/**'
+    exclude 'userguide/tutorial/antLoadfileResources/**'
     exclude '**/readme.xml'
     destDir = samplesDir
     snippetsDir = new File(buildDir, 'snippets')
@@ -86,7 +80,7 @@ task samples(type: ExtractSnippetsTask) {
         copy {
             from samplesSrcDir
             into samplesDir
-            include 'userguide/tutorial/antChecksumFiles/**'
+            include 'userguide/tutorial/antLoadfileResources/**'
         }
     }
 }
@@ -172,7 +166,7 @@ task remoteUserguideDocbook(type: UserGuideTransformTask, dependsOn: samples) {
     inputs.files fileTree(dir: docbookSrc, includes: ['*.xml'])
     sourceFile = new File(userguideSrcDir, 'userguide.xml')
     destFile = new File(docbookSrc, 'remoteUserguide.xml')
-    doFirst {
+    gradle.taskGraph.whenReady {
         javadocUrl = remoteLocations.javadocUrl
         groovydocUrl = remoteLocations.groovydocUrl
         dsldocUrl = remoteLocations.dsldocUrl
@@ -242,7 +236,7 @@ task javadoc(type: Javadoc) {
     include 'org/gradle/testfixtures/**'
     include 'org/gradle/tooling/**'
     exclude '**/internal/**'
-    options.links("http://java.sun.com/j2se/1.5.0/docs/api", "http://groovy.codehaus.org/gapi/", "http://maven.apache.org/ref/2.2.1/maven-core/apidocs",
+    options.links("http://download.oracle.com/javase/1.5.0/docs/api", "http://groovy.codehaus.org/gapi/", "http://maven.apache.org/ref/2.2.1/maven-core/apidocs",
         "http://maven.apache.org/ref/2.2.1/maven-model/apidocs")
     doFirst {
         title = "Gradle API $version"
@@ -260,6 +254,7 @@ task groovydoc(type: Groovydoc) {
     group = 'documentation'
     source groovyProjects().collect {project -> project.sourceSets.main.groovy + project.sourceSets.main.java }
     destinationDir = new File(docsDir, 'groovydoc')
+    classpath = javadoc.classpath
     includes = javadoc.includes
     excludes = javadoc.excludes
     doFirst {
@@ -272,7 +267,7 @@ task userguideFragmentSrc(type: UserGuideTransformTask, dependsOn: [userguideSty
     tags << 'standalone'
     sourceFile = new File(userguideSrcDir, 'installation.xml')
     destFile = new File(docbookSrc, 'installation.xml')
-    doFirst {
+    gradle.taskGraph.whenReady {
         javadocUrl = remoteLocations.javadocUrl
         groovydocUrl = remoteLocations.groovydocUrl
         dsldocUrl = remoteLocations.dsldocUrl
@@ -292,7 +287,7 @@ task websiteUserguideSrc(type: UserGuideTransformTask, dependsOn: [userguideStyl
     sourceFile = new File(userguideSrcDir, 'userguide.xml')
     destFile = new File(docbookSrc, 'website.xml')
     tags << 'website'
-    doFirst {
+    gradle.taskGraph.whenReady {
         javadocUrl = remoteLocations.javadocUrl
         groovydocUrl = remoteLocations.groovydocUrl
         dsldocUrl = remoteLocations.dsldocUrl
@@ -342,32 +337,20 @@ task docs {
     group = 'documentation'
 }
 
-task uploadDocs(dependsOn: docs) << {
-    ftp(action: 'mkdir', remotedir: remoteLocations.docsRemoteDir)
-    ftp(action: 'delete', remotedir: remoteLocations.docsRemoteDir) {
-        fileset() {
-            include(name: '**/*')
-        }
-    }
-    ftp(action: 'send', remotedir: remoteLocations.docsRemoteDir) {
-        fileset(dir: docsDir)
+task uploadDocs(dependsOn: docs, type: Scp) {
+    sourceDir = docsDir
+    host = 'gradle01.managed.contegix.com'
+    gradle.taskGraph.whenReady {
+        destinationDir = remoteLocations.docsUploadDir
     }
+    userName = project.hasProperty('websiteScpUserName') ? project.websiteScpUserName : null
+    password = project.hasProperty('websiteScpUserPassword') ? project.websiteScpUserPassword : null
 }
-
-void ftp(Map args, Closure antFileset = {}) {
-    ant {
-        taskdef(name: 'ftp',
-                classname: 'org.apache.tools.ant.taskdefs.optional.net.FTP',
-                classpath: configurations.ftpAntTask.asPath)
-        Map ftpArgs = args + [
-                server: 'ftp.gradle.org',
-                userid: websiteFtpUserName,
-                password: websiteFtpUserPassword
-        ]
-        delegate.ftp(ftpArgs) {
-            antFileset.delegate = delegate
-            antFileset()
-        }
+gradle.taskGraph.whenReady { graph ->
+    if (graph.hasTask(uploadDocs)) {
+        // fail early
+        project.websiteScpUserName
+        project.websiteScpUserPassword
     }
 }
 
@@ -380,6 +363,10 @@ class RemoteLocations {
         version.isRelease() ? 'https://dav.codehaus.org/dist/gradle' : 'https://dav.codehaus.org/snapshots.dist/gradle'
     }
 
+    String getDocsUploadDir() {
+        '/var/www/domains/gradle.org/www/htdocs/releases/' + docsRemoteDir
+    }
+
     String getDocsRemoteDir() {
         (version.isRelease() ? version.toString() : 'latest') + '/docs'
     }
diff --git a/subprojects/docs/src/docs/dsl/dsl.xml b/subprojects/docs/src/docs/dsl/dsl.xml
index 215a908..822af3f 100644
--- a/subprojects/docs/src/docs/dsl/dsl.xml
+++ b/subprojects/docs/src/docs/dsl/dsl.xml
@@ -149,6 +149,9 @@
                 <td>org.gradle.api.tasks.Copy</td>
             </tr>
             <tr>
+                <td>org.gradle.api.tasks.application.CreateStartScripts</td>
+            </tr>
+            <tr>
                 <td>org.gradle.api.tasks.Delete</td>
             </tr>
             <tr>
@@ -158,16 +161,19 @@
                 <td>org.gradle.api.tasks.diagnostics.DependencyReportTask</td>
             </tr>
             <tr>
-                <td>org.gradle.plugins.eclipse.EclipseClasspath</td>
+                <td>org.gradle.plugins.ide.eclipse.GenerateEclipseClasspath</td>
+            </tr>
+            <tr>
+                <td>org.gradle.plugins.ide.eclipse.GenerateEclipseJdt</td>
             </tr>
             <tr>
-                <td>org.gradle.plugins.eclipse.EclipseJdt</td>
+                <td>org.gradle.plugins.ide.eclipse.GenerateEclipseProject</td>
             </tr>
             <tr>
-                <td>org.gradle.plugins.eclipse.EclipseProject</td>
+                <td>org.gradle.plugins.ide.eclipse.GenerateEclipseWtpComponent</td>
             </tr>
             <tr>
-                <td>org.gradle.plugins.eclipse.EclipseWtp</td>
+                <td>org.gradle.plugins.ide.eclipse.GenerateEclipseWtpFacet</td>
             </tr>
             <tr>
                 <td>org.gradle.api.tasks.Exec</td>
@@ -182,13 +188,13 @@
                 <td>org.gradle.api.tasks.javadoc.Groovydoc</td>
             </tr>
             <tr>
-                <td>org.gradle.plugins.idea.IdeaModule</td>
+                <td>org.gradle.plugins.ide.idea.GenerateIdeaModule</td>
             </tr>
             <tr>
-                <td>org.gradle.plugins.idea.IdeaProject</td>
+                <td>org.gradle.plugins.ide.idea.GenerateIdeaProject</td>
             </tr>
             <tr>
-                <td>org.gradle.plugins.idea.IdeaWorkspace</td>
+                <td>org.gradle.plugins.ide.idea.GenerateIdeaWorkspace</td>
             </tr>
             <tr>
                 <td>org.gradle.api.tasks.bundling.Jar</td>
@@ -221,6 +227,9 @@
                 <td>org.gradle.api.tasks.scala.ScalaDoc</td>
             </tr>
             <tr>
+                <td>org.gradle.api.plugins.sonar.Sonar</td>
+            </tr>
+            <tr>
                 <td>org.gradle.api.tasks.Sync</td>
             </tr>
             <tr>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.ApplicationPluginConvention.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.ApplicationPluginConvention.xml
new file mode 100644
index 0000000..f9188a9
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.ApplicationPluginConvention.xml
@@ -0,0 +1,28 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr><td>Name</td>
+                    <td>Default values</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>applicationName</td>
+                <td><literal>project.name</literal></td>
+            </tr>
+            <tr>
+                <td>mainClassName</td>
+                <td><literal>null</literal></td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr><td>Name</td></tr>
+            </thead>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.antlr.AntlrSourceVirtualDirectory.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.antlr.AntlrSourceVirtualDirectory.xml
index 87988f7..3b96e77 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.antlr.AntlrSourceVirtualDirectory.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.antlr.AntlrSourceVirtualDirectory.xml
@@ -12,10 +12,6 @@
                 <td>antlr</td>
                 <td><literal>[<replaceable>${project.projectDir}</replaceable>/src/<replaceable>${sourceSet.name}</replaceable>/antlr]</literal></td>
             </tr>
-            <tr>
-                <td>allAntlr</td>
-                <td><literal>[antlr]</literal></td>
-            </tr>
         </table>
     </section>
     <section>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.sonar.Sonar.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.sonar.Sonar.xml
new file mode 100644
index 0000000..a874b70
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.sonar.Sonar.xml
@@ -0,0 +1,88 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Default with <literal>sonar</literal> and <literal>java</literal> plugins</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>serverUrl</td>
+                <td><literal>http://localhost:9000</literal></td>
+            </tr>
+            <tr>
+                <td>bootstrapDir</td>
+                <td>Gradle-managed per-project cache directory</td>
+            </tr>
+            <tr>
+                <td>projectDir</td>
+                <td><literal>project.projectDir</literal></td>
+            </tr>
+            <tr>
+                <td>projectMainSourceDirs</td>
+                <td><literal>project.sourceSets.main.java.srcDirs</literal></td>
+            </tr>
+            <tr>
+                <td>projectTestSourceDirs</td>
+                <td>
+                    <literal>project.sourceSets.main.java.srcDirs</literal>
+                </td>
+            </tr>
+            <tr>
+                <td>projectClassesDirs</td>
+                <td><literal>project.sourceSets.main.classesDir</literal></td>
+            </tr>
+            <tr>
+                <td>projectDependencies</td>
+                <td><literal>project.configurations.compile.resolve()</literal></td></tr>
+            <tr>
+                <td>projectKey</td>
+                <td><literal><replaceable>$project.group</replaceable>:<replaceable>$project.name</replaceable></literal></td>
+            </tr>
+            <tr>
+                <td>projectName</td>
+                <td><literal>project.name</literal></td>
+            </tr>
+            <tr>
+                <td>projectDescription</td>
+                <td><literal>project.description</literal></td>
+            </tr>
+            <tr>
+                <td>projectVersion</td>
+                <td><literal>project.version</literal></td>
+            </tr>
+            <tr>
+                <td>projectProperties</td>
+                <td>
+                    <literal>["sonar.java.source": project.sourceCompatibility,
+                        "sonar.java.target": project.targetCompatibility,
+                        "sonar.dynamicAnalysis": "reuseReports",
+                        "sonar.surefire.reportsPath": project.test.testResultsDir]
+                    </literal>
+                </td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr><td>Name</td></tr>
+            </thead>
+            <tr><td>projectMainSourceDir</td></tr>
+            <tr><td>projectMainSourceDirs</td></tr>
+            <tr><td>projectTestSourceDir</td></tr>
+            <tr><td>projectTestSourceDirs</td></tr>
+            <tr><td>projectClassesDir</td> </tr>
+            <tr><td>projectClassesDirs</td></tr>
+            <tr><td>projectDependency</td></tr>
+            <tr><td>projectDependencies</td></tr>
+            <tr><td>globalProperty</td></tr>
+            <tr><td>globalProperties</td></tr>
+            <tr><td>projectProperty</td></tr>
+            <tr><td>projectProperties</td></tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.application.CreateStartScripts.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.application.CreateStartScripts.xml
new file mode 100644
index 0000000..84602cb
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.application.CreateStartScripts.xml
@@ -0,0 +1,23 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr><td>Name</td></tr>
+            </thead>
+            <tr><td>applicationName</td></tr>
+            <tr><td>mainClassName</td></tr>
+            <tr><td>classpath</td></tr>
+            <tr><td>outputDir</td></tr>
+            <tr><td>optsEnvironmentVar</td></tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr><td>Name</td></tr>
+            </thead>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.wrapper.Wrapper.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.wrapper.Wrapper.xml
index 7e6a24b..d8fd6f8 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.wrapper.Wrapper.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.wrapper.Wrapper.xml
@@ -38,9 +38,9 @@
             </tr>
             <tr>
                 <td>distributionUrl</td>
-                <td><literal>'http://gradle.artifactoryonline.com/gradle/distributions/gradle-${gradleVersion}-bin.zip'</literal>
+                <td><literal>'http://repo.gradle.org/gradle/distributions/gradle-${gradleVersion}-bin.zip'</literal>
                     (or
-                    <literal>'http://gradle.artifactoryonline.com/gradle/distributions/gradle-snapshots/gradle-${gradleVersion}-bin.zip'</literal>
+                    <literal>'http://repo.gradle.org/gradle/distributions/gradle-snapshots/gradle-${gradleVersion}-bin.zip'</literal>
                     for snapshot versions).
                 </td>
             </tr>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.plugins.eclipse.EclipseClasspath.xml b/subprojects/docs/src/docs/dsl/org.gradle.plugins.eclipse.EclipseClasspath.xml
deleted file mode 100644
index 38b06a5..0000000
--- a/subprojects/docs/src/docs/dsl/org.gradle.plugins.eclipse.EclipseClasspath.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-<section>
-    <section>
-        <title>Properties</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                    <td>Default with <literal>eclipse</literal> and <literal>java</literal> plugins</td>
-                </tr>
-            </thead>
-            <tr>
-                <td>sourceSets</td>
-                <td><literal>project.sourceSets</literal></td>
-            </tr>
-            <tr>
-                <td>plusConfigurations</td>
-                <td><literal>project.configurations.testRuntime</literal></td>
-            </tr>
-            <tr>
-                <td>minusConfigurations</td>
-                <td><literal>[]</literal></td>
-            </tr>
-            <tr>
-                <td>variables</td>
-                <td><literal>[:]</literal></td>
-            </tr>
-            <tr>
-                <td>containers</td>
-                <td>[JRE container]</td>
-            </tr>
-            <tr>
-                <td>defaultOutputDir</td>
-                <td><literal>project.sourceSets.main.classesDir</literal></td>
-            </tr>
-            <tr>
-                <td>downloadSources</td>
-                <td><literal>true</literal></td>
-            </tr>
-            <tr>
-                <td>downloadJavadoc</td>
-                <td><literal>false</literal></td>
-            </tr>
-            <tr>
-                <td>outputFile</td>
-                <td><filename><replaceable>${project.projectDir}</replaceable>/.classpath</filename></td>
-            </tr>
-        </table>
-    </section>
-    <section>
-        <title>Methods</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                </tr>
-            </thead>
-            <tr>
-                <td>containers</td>
-            </tr>
-            <tr>
-                <td>variables</td>
-            </tr>
-        </table>
-    </section>
-</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.plugins.eclipse.EclipseWtp.xml b/subprojects/docs/src/docs/dsl/org.gradle.plugins.eclipse.EclipseWtp.xml
deleted file mode 100644
index 0a47334..0000000
--- a/subprojects/docs/src/docs/dsl/org.gradle.plugins.eclipse.EclipseWtp.xml
+++ /dev/null
@@ -1,96 +0,0 @@
-<section>
-    <section>
-        <title>Properties</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                    <td>Default with <literal>eclipse</literal> and <literal>war</literal> plugins</td>
-                </tr>
-            </thead>
-            <tr>
-                <td>orgEclipseWstCommonComponentInputFile</td>
-                <td/>
-            </tr>
-            <tr>
-                <td>orgEclipseWstCommonComponentOutputFile</td>
-                <td><filename><replaceable>${project.projectDir}</replaceable>/.settings/org.eclipse.wst.common.component</filename></td>
-            </tr>
-            <tr>
-                <td>orgEclipseWstCommonProjectFacetCoreInputFile</td>
-                <td/>
-            </tr>
-            <tr>
-                <td>orgEclipseWstCommonProjectFacetCoreOutputFile</td>
-                <td><filename><replaceable>${project.projectDir}</replaceable>/.settings/org.eclipse.wst.common.project.facet.core.xml</filename></td>
-            </tr>
-            <tr>
-                <td>sourceSets</td>
-                <td><literal>[project.sourceSets.main]</literal></td>
-            </tr>
-            <tr>
-                <td>plusConfigurations</td>
-                <td><literal>[project.configurations.runtime]</literal></td>
-            </tr>
-            <tr>
-                <td>minusConfigurations</td>
-                <td><literal>[project.configurations.providedRuntime]</literal></td>
-            </tr>
-            <tr>
-                <td>facets</td>
-                <td>Java and web facets.</td>
-            </tr>
-            <tr>
-                <td>deployName</td>
-                <td><literal>project.name</literal></td>
-            </tr>
-            <tr>
-                <td>variables</td>
-                <td><literal>[:]</literal></td>
-            </tr>
-            <tr>
-                <td>resources</td>
-                <td><literal>[deployPath: '/', sourcePath: project.webAppDirName]</literal></td>
-            </tr>
-            <tr>
-                <td>properties</td>
-                <td><literal>[]</literal></td>
-            </tr>
-            <tr>
-                <td>contextPath</td>
-                <td><literal>project.war.baseName</literal></td>
-            </tr>
-        </table>
-    </section>
-    <section>
-        <title>Methods</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                </tr>
-            </thead>
-            <tr>
-                <td>facet</td>
-            </tr>
-            <tr>
-                <td>variables</td>
-            </tr>
-            <tr>
-                <td>property</td>
-            </tr>
-            <tr>
-                <td>resource</td>
-            </tr>
-            <tr>
-                <td>withXml</td>
-            </tr>
-            <tr>
-                <td>beforeConfigured</td>
-            </tr>
-            <tr>
-                <td>whenConfigured</td>
-            </tr>
-        </table>
-    </section>
-</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.GeneratorTask.xml b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.api.GeneratorTask.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.api.tasks.GeneratorTask.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.api.GeneratorTask.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.XmlGeneratorTask.xml b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.api.XmlGeneratorTask.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.api.tasks.XmlGeneratorTask.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.api.XmlGeneratorTask.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseClasspath.xml b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseClasspath.xml
new file mode 100644
index 0000000..f32b8ab
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseClasspath.xml
@@ -0,0 +1,65 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Default with <literal>eclipse</literal> and <literal>java</literal> plugins</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>sourceSets</td>
+                <td><literal>project.sourceSets</literal></td>
+            </tr>
+            <tr>
+                <td>plusConfigurations</td>
+                <td><literal>project.configurations.testRuntime</literal></td>
+            </tr>
+            <tr>
+                <td>minusConfigurations</td>
+                <td><literal>[]</literal></td>
+            </tr>
+            <tr>
+                <td>variables</td>
+                <td><literal>[:]</literal></td>
+            </tr>
+            <tr>
+                <td>containers</td>
+                <td>[JRE container]</td>
+            </tr>
+            <tr>
+                <td>defaultOutputDir</td>
+                <td><filename><replaceable>${project.projectDir}</replaceable>/bin</filename></td>
+            </tr>
+            <tr>
+                <td>downloadSources</td>
+                <td><literal>true</literal></td>
+            </tr>
+            <tr>
+                <td>downloadJavadoc</td>
+                <td><literal>false</literal></td>
+            </tr>
+            <tr>
+                <td>outputFile</td>
+                <td><filename><replaceable>${project.projectDir}</replaceable>/.classpath</filename></td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>containers</td>
+            </tr>
+            <tr>
+                <td>variables</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.plugins.eclipse.EclipseJdt.xml b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseJdt.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.plugins.eclipse.EclipseJdt.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseJdt.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.plugins.eclipse.EclipseProject.xml b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseProject.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.plugins.eclipse.EclipseProject.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseProject.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseWtpComponent.xml b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseWtpComponent.xml
new file mode 100644
index 0000000..ff7d9bd
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseWtpComponent.xml
@@ -0,0 +1,72 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Default with <literal>eclipse</literal> and <literal>war</literal> plugins</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>inputFile</td>
+                <td/>
+            </tr>
+            <tr>
+                <td>outputFile</td>
+                <td><filename><replaceable>${project.projectDir}</replaceable>/.settings/org.eclipse.wst.common.component</filename></td>
+            </tr>
+            <tr>
+                <td>sourceDirs</td>
+                <td>source dirs from <literal>project.sourceSets.main.allSource</literal></td>
+            </tr>
+            <tr>
+                <td>plusConfigurations</td>
+                <td><literal>[project.configurations.runtime]</literal></td>
+            </tr>
+            <tr>
+                <td>minusConfigurations</td>
+                <td><literal>[project.configurations.providedRuntime]</literal></td>
+            </tr>
+            <tr>
+                <td>deployName</td>
+                <td><literal>project.name</literal></td>
+            </tr>
+            <tr>
+                <td>variables</td>
+                <td><literal>[:]</literal></td>
+            </tr>
+            <tr>
+                <td>resources</td>
+                <td><literal>[deployPath: '/', sourcePath: project.webAppDirName]</literal></td>
+            </tr>
+            <tr>
+                <td>properties</td>
+                <td><literal>[]</literal></td>
+            </tr>
+            <tr>
+                <td>contextPath</td>
+                <td><literal>project.war.baseName</literal></td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>variables</td>
+            </tr>
+            <tr>
+                <td>property</td>
+            </tr>
+            <tr>
+                <td>resource</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseWtpFacet.xml b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseWtpFacet.xml
new file mode 100644
index 0000000..81025d7
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseWtpFacet.xml
@@ -0,0 +1,38 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Default with <literal>eclipse</literal> and <literal>war</literal> plugins</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>inputFile</td>
+                <td/>
+            </tr>
+            <tr>
+                <td>outputFile</td>
+                <td><filename><replaceable>${project.projectDir}</replaceable>/.settings/org.eclipse.wst.common.project.facet.core.xml</filename></td>
+            </tr>
+            <tr>
+                <td>facets</td>
+                <td>Java and web facets.</td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>facet</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.idea.GenerateIdeaModule.xml b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.idea.GenerateIdeaModule.xml
new file mode 100644
index 0000000..9af8844
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.idea.GenerateIdeaModule.xml
@@ -0,0 +1,103 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Default with <literal>idea</literal> plugin</td>
+                    <td>Default with <literal>idea</literal> and <literal>java</literal> plugin</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>moduleDir</td>
+                <td><literal>project.projectDir</literal></td>
+                <td/>
+            </tr>
+            <tr>
+                <td>sourceDirs</td>
+                <td><literal>[]</literal></td>
+                <td>source dirs from <literal>project.sourceSets.main.allSource</literal></td>
+            </tr>
+            <tr>
+                <td>testSourceDirs</td>
+                <td><literal>[]</literal></td>
+                <td>source dirs from <literal>project.sourceSets.test.allSource</literal></td>
+            </tr>
+            <tr>
+                <td>excludeDirs</td>
+                <td><literal>[project.buildDir, project.file('.gradle')]</literal></td>
+                <td/>
+            </tr>
+            <tr>
+                <td>inheritOutputDirs</td>
+                <td><literal>null</literal></td>
+                <td><literal>null</literal></td>
+            </tr>
+            <tr>
+                <td>outputDir</td>
+                <td><literal>null</literal></td>
+                <td><literal>null</literal></td>
+            </tr>
+            <tr>
+                <td>testOutputDir</td>
+                <td><literal>null</literal></td>
+                <td><literal>null</literal></td>
+            </tr>
+            <tr>
+                <td>javaVersion</td>
+                <td><literal>'inherited'</literal></td>
+                <td/>
+            </tr>
+            <tr>
+                <td>downloadSources</td>
+                <td><literal>true</literal></td>
+                <td/>
+            </tr>
+            <tr>
+                <td>downloadJavadoc</td>
+                <td><literal>false</literal></td>
+                <td/>
+            </tr>
+            <tr>
+                <td>variables</td>
+                <td><literal>[:]</literal></td>
+                <td/>
+            </tr>
+            <tr>
+                <td>scopes</td>
+                <td><literal>[:]</literal></td>
+                <td>
+                    <itemizedlist>
+                        <listitem><literal>COMPILE</literal> -> <literal>project.configurations.compile</literal></listitem>
+                        <listitem><literal>RUNTIME</literal> -> <literal>project.configurations.runtime - project.configurations.compile</literal></listitem>
+                        <listitem><literal>TEST</literal> -> <literal>project.configurations.testRuntime - project.configurations.runtime</literal></listitem>
+                    </itemizedlist>
+                </td>
+            </tr>
+            <tr>
+                <td>outputFile</td>
+                <td><literal><replaceable>${project.projectDir}</replaceable>/<replaceable>${project.name}</replaceable>.iml</literal>
+                    (sometimes the <replaceable>project.name</replaceable> is prefixed with parts of <replaceable>${project.path}</replaceable> to guarantee uniqeness).
+                    Bear in mind that usually it is easier to use moduleName property instead of outputFile property.
+                </td>
+                <td/>
+            </tr>
+            <tr>
+                <td>moduleName</td>
+                <td><literal><replaceable>${project.name}</replaceable> (sometimes prefixed with parts of <replaceable>${project.path}</replaceable> to guarantee uniqeness)</literal></td>
+                <td/>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.plugins.idea.IdeaProject.xml b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.idea.GenerateIdeaProject.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.plugins.idea.IdeaProject.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.idea.GenerateIdeaProject.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.plugins.idea.IdeaWorkspace.xml b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.idea.GenerateIdeaWorkspace.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.plugins.idea.IdeaWorkspace.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.idea.GenerateIdeaWorkspace.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.plugins.idea.IdeaModule.xml b/subprojects/docs/src/docs/dsl/org.gradle.plugins.idea.IdeaModule.xml
deleted file mode 100644
index 82b429d..0000000
--- a/subprojects/docs/src/docs/dsl/org.gradle.plugins.idea.IdeaModule.xml
+++ /dev/null
@@ -1,90 +0,0 @@
-<section>
-    <section>
-        <title>Properties</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                    <td>Default with <literal>idea</literal> plugin</td>
-                    <td>Default with <literal>idea</literal> and <literal>java</literal> plugin</td>
-                </tr>
-            </thead>
-            <tr>
-                <td>moduleDir</td>
-                <td><literal>project.projectDir</literal></td>
-                <td/>
-            </tr>
-            <tr>
-                <td>sourceDirs</td>
-                <td><literal>[]</literal></td>
-                <td>source dirs from <literal>project.sourceSets.main.allSource</literal></td>
-            </tr>
-            <tr>
-                <td>testSourceDirs</td>
-                <td><literal>[]</literal></td>
-                <td>source dirs from <literal>project.sourceSets.test.allSource</literal></td>
-            </tr>
-            <tr>
-                <td>excludeDirs</td>
-                <td><literal>[project.buildDir, project.file('.gradle')]</literal></td>
-                <td/>
-            </tr>
-            <tr>
-                <td>outputDir</td>
-                <td><literal>null</literal></td>
-                <td><literal>project.sourceSets.main.classesDir</literal></td>
-            </tr>
-            <tr>
-                <td>testOutputDir</td>
-                <td><literal>null</literal></td>
-                <td><literal>project.sourceSets.test.classesDir</literal></td>
-            </tr>
-            <tr>
-                <td>javaVersion</td>
-                <td><literal>'inherited'</literal></td>
-                <td/>
-            </tr>
-            <tr>
-                <td>downloadSources</td>
-                <td><literal>true</literal></td>
-                <td/>
-            </tr>
-            <tr>
-                <td>downloadJavadoc</td>
-                <td><literal>false</literal></td>
-                <td/>
-            </tr>
-            <tr>
-                <td>variables</td>
-                <td><literal>[:]</literal></td>
-                <td/>
-            </tr>
-            <tr>
-                <td>scopes</td>
-                <td><literal>[:]</literal></td>
-                <td>
-                    <itemizedlist>
-                        <listitem><literal>COMPILE</literal> -> <literal>project.configurations.compile</literal></listitem>
-                        <listitem><literal>RUNTIME</literal> -> <literal>project.configurations.runtime - project.configurations.compile</literal></listitem>
-                        <listitem><literal>TEST</literal> -> <literal>project.configurations.testRuntime - project.configurations.runtime</literal></listitem>
-                    </itemizedlist>
-                </td>
-            </tr>
-            <tr>
-                <td>outputFile</td>
-                <td><literal><replaceable>${project.projectDir}</replaceable>/<replaceable>${project.name}</replaceable>.iml</literal></td>
-                <td/>
-            </tr>
-        </table>
-    </section>
-    <section>
-        <title>Methods</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                </tr>
-            </thead>
-        </table>
-    </section>
-</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/plugins.xml b/subprojects/docs/src/docs/dsl/plugins.xml
index 5a14276..95fe0af 100644
--- a/subprojects/docs/src/docs/dsl/plugins.xml
+++ b/subprojects/docs/src/docs/dsl/plugins.xml
@@ -35,4 +35,7 @@
     <plugin id="osgi" description="OSGi Plugin">
         <extends targetClass="org.gradle.api.Project" extensionClass="org.gradle.api.plugins.osgi.OsgiPluginConvention"/>
     </plugin>
+    <plugin id="application" description="Application Plugin">
+        <extends targetClass="org.gradle.api.Project" extensionClass="org.gradle.api.plugins.ApplicationPluginConvention"/>
+    </plugin>
 </plugins>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/userguide/announcePlugin.xml b/subprojects/docs/src/docs/userguide/announcePlugin.xml
index d8637dd7..fee4f01 100644
--- a/subprojects/docs/src/docs/userguide/announcePlugin.xml
+++ b/subprojects/docs/src/docs/userguide/announcePlugin.xml
@@ -13,14 +13,14 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<chapter id='announce_plugin' xmlns:xi="http://www.w3.org/2001/XInclude">
+<chapter id='announce_plugin'>
     <title>The Announce Plugin</title>
     <para>The Gradle announce plugin enables you to publish messages on succeeded tasks to your favourite platforms.
 	It supports 
 	<itemizedlist>
  		<listitem><ulink url='http://twitter.com'>Twitter</ulink></listitem>
  		<listitem><ulink url='http://ubuntu.com'>Ubuntu Notify</ulink></listitem>
-        <listitem><ulink url="http://www.fullphat.net/index.php">Snarl</ulink>, a Windows Notification System</listitem>
+        <listitem><ulink url="https://sites.google.com/site/snarlapp/home">Snarl</ulink>, a Windows Notification System</listitem>
         <listitem><ulink url="http://growl.info/">Growl</ulink>, a Mac OS X Notification System</listitem>
  	</itemizedlist>
 	
diff --git a/subprojects/docs/src/docs/userguide/applicationPlugin.xml b/subprojects/docs/src/docs/userguide/applicationPlugin.xml
new file mode 100644
index 0000000..4294b26
--- /dev/null
+++ b/subprojects/docs/src/docs/userguide/applicationPlugin.xml
@@ -0,0 +1,105 @@
+<!--
+  ~ Copyright 2011 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<chapter id='application_plugin'>
+    <title>The Application Plugin</title>
+    <para>The Gradle application plugin extends the language plugins with common application related tasks.
+	It allows running and bundling applications for the jvm.
+	</para>
+
+    <section>
+        <title>Usage</title>
+        <para>To use the application plugin, include in your build script:</para>
+        <sample id="useApplicationPlugin" dir="application" title="Using the application plugin">
+            <sourcefile file="build.gradle" snippet="use-plugin"/>
+        </sample>
+        <para>To define the main-class for the application you have to set the <literal>mainClassName</literal> property as shown below</para>
+        <sample id="useApplicationPlugin" dir="application" title="Configure the application main class">
+            <sourcefile file="build.gradle" snippet="mainClassName-conf"/>
+        </sample>
+
+        <para>Then, you can run the application by running <userinput>gradle run</userinput>. Gradle will take care of building the application classes,
+            along with their runtime dependencies, and starting the application with the correct classpath.
+        </para>
+
+        <para>The plugin can also build a distribution for your application. The distribution will package up the runtime dependencies of the application
+            along with some OS specific start scripts. You can run <userinput>gradle install</userinput> to create an image of the application in
+            <filename>build/install/<replaceable>projectName</replaceable></filename>. You can run <userinput>gradle distZip</userinput> to create a
+            ZIP containing the distribution.
+        </para>
+    </section>
+
+    <section>
+        <title>Tasks</title>
+        <para>The Application plugin adds the following tasks to the project.</para>
+        <table>
+            <title>Application plugin - tasks</title>
+            <thead>
+                <tr>
+                    <td>Task name</td>
+                    <td>Depends on</td>
+                    <td>Type</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>
+                    <literal>run</literal>
+                </td>
+                <td>
+                    <literal>classes</literal>
+                </td>
+                <td><apilink class="org.gradle.api.tasks.JavaExec"/></td>
+                <td>Starts the application.</td>
+            </tr>
+            <tr>
+                <td>
+                    <literal>startScripts</literal>
+                </td>
+                <td>
+                    <literal>jar</literal>
+                </td>
+                <td><apilink class="org.gradle.api.tasks.application.CreateStartScripts"/></td>
+                <td>Creates OS specific scripts to run the project as a JVM application.</td>
+            </tr>
+            <tr>
+                <td>
+                    <literal>install</literal>
+                </td>
+                <td>
+                    <literal>jar</literal>, <literal>startScripts</literal>
+                </td>
+                <td><apilink class="org.gradle.api.tasks.Sync"/></td>
+                <td>Installs the application into a specified directory.</td>
+            </tr>
+            <tr>
+                <td>
+                    <literal>distZip</literal>
+                </td>
+                <td>
+                    <literal>jar</literal>, <literal>startScripts</literal>
+                </td>
+                <td><apilink class="org.gradle.api.tasks.bundling.Zip"/></td>
+                <td>Creates a full distribution ZIP archive including runtime libraries and OS specific scripts.</td>
+            </tr>
+        </table>
+    </section>
+
+    <section>
+            <title>Convention properties</title>
+            <para>The application plugin adds some properties to the project, which you can use to configure its behaviour. See <apilink class="org.gradle.api.Project"/>.
+            </para>
+    </section>
+</chapter>
diff --git a/subprojects/docs/src/docs/userguide/buildLifecycle.xml b/subprojects/docs/src/docs/userguide/buildLifecycle.xml
index a195c13..ddfcd30 100644
--- a/subprojects/docs/src/docs/userguide/buildLifecycle.xml
+++ b/subprojects/docs/src/docs/userguide/buildLifecycle.xml
@@ -113,7 +113,7 @@
                     <sourcefile file="settings.gradle" snippet="hierarchical-layout"/>
                 </sample>
                 <para>The <literal>include</literal> method takes as an argument a relative virtual path to the root
-                    project. This relative virtual path is assumed to be equals to the relative physical path of the
+                    project. This relative virtual path is assumed to be equal to the relative physical path of the
                     subproject to the root project. You only need to specify the leafs of the tree. Each parent path of
                     the leaf project is assumed to be another subproject which obeys to the physical path assumption
                     described above.
diff --git a/subprojects/docs/src/docs/userguide/buildScriptsTutorial.xml b/subprojects/docs/src/docs/userguide/buildScriptsTutorial.xml
index 2dec7e2..0cdb16f 100644
--- a/subprojects/docs/src/docs/userguide/buildScriptsTutorial.xml
+++ b/subprojects/docs/src/docs/userguide/buildScriptsTutorial.xml
@@ -172,11 +172,11 @@
         <para>Ant tasks are first-class citizens in Gradle. Gradle provides excellent integration for Ant tasks simply
             by relying on Groovy. Groovy is shipped with the fantastic <literal>AntBuilder</literal>. Using Ant tasks
             from Gradle is as convenient and more powerful than using Ant tasks from a <filename>build.xml</filename>
-            file. Let's look at an example:
+            file. From below example you can learn how to execute ant tasks and how to access ant properties:
         </para>
-        <sample id="antChecksum" dir="userguide/tutorial/antChecksum" title="Using AntBuilder to execute ant.checksum target">
+        <sample id="antLoadfile" dir="userguide/tutorial/antLoadfile" title="Using AntBuilder to execute ant.loadfile target">
             <sourcefile file="build.gradle"/>
-            <output args="-q checksum"/>
+            <output args="-q loadfile"/>
         </sample>
 
         <para>There is lots more you can do with Ant in your build scripts. You can find out more in <xref linkend="ant"/>.</para>
@@ -186,9 +186,9 @@
         <para>Gradle scales in how you can organize your build logic. The first level of organizing your build logic for
             the example above, is extracting a method.
         </para>
-        <sample id="antChecksumWithMethod" dir="userguide/tutorial/antChecksumWithMethod" title="Using methods to organize your build logic">
+        <sample id="antLoadfileWithMethod" dir="userguide/tutorial/antLoadfileWithMethod" title="Using methods to organize your build logic">
             <sourcefile file="build.gradle"/>
-            <output args="-q checksum"/>
+            <output args="-q loadfile"/>
         </sample>
         <para>Later you will see that such methods can be shared among subprojects in multi-project builds. If your
             build logic becomes more complex, Gradle offers you other very convenient ways to organize it. We have
diff --git a/subprojects/docs/src/docs/userguide/customPlugins.xml b/subprojects/docs/src/docs/userguide/customPlugins.xml
index 617a228..6c89061 100644
--- a/subprojects/docs/src/docs/userguide/customPlugins.xml
+++ b/subprojects/docs/src/docs/userguide/customPlugins.xml
@@ -147,7 +147,19 @@
             <sourcefile file="build.gradle" snippet="use-plugin"/>
         </sample>
 
-        <para>We just follow the convention for where the source for the plugin class should go.</para>
+	<para>
+            So how does Gradle find the <apilink class="org.gradle.api.Plugin"/> implementation? The answer is you need to provide a properties file in the jar's
+	    <filename>META-INF/gradle-plugins</filename> directory that matches the name of your plugin.
+	</para>
+
+        <sample id="customPluginStandalone" dir="customPlugin" title="Wiring for a custom plugin">
+            <sourcefile file="src/main/resources/META-INF/gradle-plugins/greeting.properties"/>
+        </sample>	
+
+	<para>
+	    Notice that the properties filename matches the plugin's name and is placed in the resources folder, and
+	    that the <literal>implementation-class</literal> property identifies the <apilink class="org.gradle.api.Plugin"/> implementation class.
+	</para>
 
         <section>
             <title>Using your plugin in another project</title>
diff --git a/subprojects/docs/src/docs/userguide/depMngmt.xml b/subprojects/docs/src/docs/userguide/depMngmt.xml
index 1153b3c..8350081 100644
--- a/subprojects/docs/src/docs/userguide/depMngmt.xml
+++ b/subprojects/docs/src/docs/userguide/depMngmt.xml
@@ -374,11 +374,11 @@
             <output args="-q files"/>
         </sample>
         <para>The <code>Configuration.files</code> method always retrieves all artifacts of the <emphasis>whole</emphasis> configuration. It
-        then filters the retrieved files by specified dpendencies. As you can see in the example, transitive dependencies are included.
+        then filters the retrieved files by specified dependencies. As you can see in the example, transitive dependencies are included.
         </para>
         <para>You can also copy a configuration. You can optionally specify that only a subset of dependencies from the orginal configuration
             should be copied. The copying methods come in two flavors. The <code>copy</code> method copies only the dependencies belonging
-            explicitly to the configuration. The <code>copyRecursive</code> methode copies all the dependencies, including the dependencies from extended
+            explicitly to the configuration. The <code>copyRecursive</code> method copies all the dependencies, including the dependencies from extended
             configurations.
         </para>
         <sample id="configurationHandlingCopy" dir="userguide/artifacts/configurationHandling" title="Configuration.copy">
@@ -472,6 +472,22 @@
             </para>
         </section>
         <section>
+            <title>Ivy repositories</title>
+            <para>To use an Ivy repository:</para>
+            <sample id="ivyRepository" dir="userguide/artifacts/defineRepository" title="Ivy repository">
+                <sourcefile file="build.gradle" snippet="ivy-repo"/>
+            </sample>
+            <para>See <apilink class="org.gradle.api.artifacts.dsl.IvyArtifactRepository"/> for details.</para>
+            <section>
+                <title>Accessing password protected Ivy repositories</title>
+                <para>To access an Ivy repository which uses basic authentication, you specify the username and password to use when you define the repository:
+                </para>
+                <sample id="ivyRepository" dir="userguide/artifacts/defineRepository" title="Ivy repository">
+                    <sourcefile file="build.gradle" snippet="authenticated-ivy-repo"/>
+                </sample>
+            </section>
+        </section>
+        <section>
             <title>More about preconfigured repositories</title>
             <para>The methods above for creating preconfigured repositories share some common behavior. For all of them, defining
                 a name for the repository is optional. If no name is defined a default name is calculated, depending on the
@@ -485,10 +501,9 @@
         </section>
         <section id='sub:cache'>
             <title>Cache</title>
-            <para>When Gradle downloads dependencies from remote repositories it stores them in a local cache located at
-                <literal>USER_HOME/.gradle/cache</literal>. When Gradle downloads dependencies from one of its
-                predefined local resolvers (e.g. Flat Directory resolver), the cache is not used as an intermediate
-                storage for dependency artifacts. The cache is always used for caching module descriptors.
+            <para>When Gradle downloads artifacts from remote repositories it stores them in a local cache located at
+                <literal>USER_HOME/.gradle/cache</literal>. When Gradle downloads artifacts from one of its
+                predefined local resolvers (e.g. flat directory resolver), the cache is not used.
             </para>
         </section>
         <section id='sub:more_about_ivy_resolvers'>
diff --git a/subprojects/docs/src/docs/userguide/eclipsePlugin.xml b/subprojects/docs/src/docs/userguide/eclipsePlugin.xml
index 040d011..3cb95cd 100644
--- a/subprojects/docs/src/docs/userguide/eclipsePlugin.xml
+++ b/subprojects/docs/src/docs/userguide/eclipsePlugin.xml
@@ -17,13 +17,13 @@
 <chapter id="eclipse_plugin">
     <title>The Eclipse Plugin</title>
     
-    <para>The Eclipse plugin generates files that are used by <ulink url="http://eclipse.org">Eclipse IDE</ulink>, thus
+    <para>The Eclipse plugin generates files that are used by the <ulink url="http://eclipse.org">Eclipse IDE</ulink>, thus
         making it possible to import the project into Eclipse (<guimenuitem>File</guimenuitem> - <guimenuitem>Import...</guimenuitem> - <guimenuitem>Existing Projects into Workspace</guimenuitem>).
-        Both external and project dependencies are considered.</para>
+        Both external dependencies (including associated source and javadoc files) and project dependencies are considered.</para>
 
-    <para>The Eclipse plugin will create different files depending on which other plugins used:</para>
+    <para>What exactly the Eclipse plugin generates depends on which other plugins are used:</para>
     <table>
-        <title>Eclipse support for other plugins</title>
+        <title>Eclipse plugin behavior</title>
         <thead>
             <tr><td>Plugin</td><td>Description</td></tr>
         </thead>
@@ -38,23 +38,27 @@
             <td><link linkend="groovy_plugin">Groovy</link></td><td>Adds Groovy configuration to <filename>.project</filename> file.</td>
         </tr>
         <tr>
-            <td><link linkend="scala_plugin">Scala</link></td><td>Adds Scala support to <filename>.project</filename> file.</td>
+                <td><link linkend="scala_plugin">Scala</link></td><td>Adds Scala support to <filename>.project</filename> file.</td>
         </tr>
         <tr>
-            <td><link linkend="war_plugin">War</link></td><td>Adds web application support to <filename>.project</filename>.
+            <td><link linkend="war_plugin">War</link></td><td>Adds web application support to <filename>.project</filename> file.
             Generates WTP settings files.</td>
         </tr>
     </table>
 
-<section>
-        <title>Usage</title>
-    <para>To use the Eclipse plugin, include in your build script:</para>
-    <sample id="useEclipsePlugin" dir="eclipse" title="Using the Eclipse plugin">
-        <sourcefile file="build.gradle" snippet="use-plugin"/>
-    </sample>
-    <para>There are several tasks that the Eclipse plugin provides (presented in <xref linkend='eclipsetasks'/>). The
-        main task that you'd use is the <literal>eclipse</literal> task.
+    <para>One focus of the Eclipse plugin is to be open to customization. Each task provides a standardized set of hooks
+        for adding and removing content from the generated files.
     </para>
+
+    <section>
+        <title>Usage</title>
+        <para>To use the Eclipse plugin, include this in your build script:</para>
+        <sample id="useEclipsePlugin" dir="eclipse" title="Using the Eclipse plugin">
+            <sourcefile file="build.gradle" snippet="use-plugin"/>
+        </sample>
+        <para>The Eclipse plugin adds a number of tasks to your projects. The main tasks that you will use
+            are the <literal>eclipse</literal> and<literal>cleanEclipse</literal> tasks.
+        </para>
     </section>
     <section>
         <title>Tasks</title>
@@ -76,9 +80,9 @@
                     <literal>eclipse</literal>
                 </td>
                 <td><literal>eclipseProject</literal>, <literal>eclipseClasspath</literal>, <literal>eclipseJdt</literal>,
-                    <literal>eclipseWtp</literal></td>
+                    <literal>eclipseWtpComponent</literal>, <literal>cleanEclipseWtpFacet</literal></td>
                 <td><apilink class="org.gradle.api.Task"/></td>
-                <td>Generates all the eclipse configuration files</td>
+                <td>Generates all Eclipse configuration files</td>
             </tr>
             <tr>
                 <td>
@@ -86,10 +90,10 @@
                 </td>
                 <td>
                     <literal>cleanEclipseProject</literal>, <literal>cleanEclipseClasspath</literal>, <literal>cleanEclipseJdt</literal>,
-                    <literal>cleanEclipseWtp</literal>
+                    <literal>cleanEclipseWtpComponent</literal>, <literal>cleanEclipseWtpFacet</literal>
                 </td>
                 <td><apilink class="org.gradle.api.tasks.Delete"/></td>
-                <td>Removes all eclipse configuration files</td>
+                <td>Removes all Eclipse configuration files</td>
             </tr>
             <tr>
                 <td>
@@ -99,7 +103,7 @@
                     <literal>-</literal>
                 </td>
                 <td><apilink class="org.gradle.api.tasks.Delete"/></td>
-                <td>Removes the eclipse project file</td>
+                <td>Generates the <filename>.project</filename> file.</td>
             </tr>
             <tr>
                 <td>
@@ -109,7 +113,7 @@
                     <literal>-</literal>
                 </td>
                 <td><apilink class="org.gradle.api.tasks.Delete"/></td>
-                <td>Removes the eclipse classpath file</td>
+                <td>Generates the <filename>.classpath</filename> file.</td>
             </tr>
             <tr>
                 <td>
@@ -119,17 +123,30 @@
                     <literal>-</literal>
                 </td>
                 <td><apilink class="org.gradle.api.tasks.Delete"/></td>
-                <td>Removes the JDT settings file.</td>
+                <td>Removes the <filename>.settings/org.eclipse.jdt.core.prefs</filename> file.</td>
             </tr>
             <tr>
                 <td>
-                    <literal>cleanEclipseWtp</literal>
+                    <literal>cleanEclipseWtpComponent</literal>
                 </td>
                 <td>
                     <literal>-</literal>
                 </td>
                 <td><apilink class="org.gradle.api.tasks.Delete"/></td>
-                <td>Removes the WTP settings files.</td>
+                <td>Removes the <filename>.settings/org.eclipse.wst.common.component</filename> file.</td>
+            </tr>
+            <tr>
+                <td>
+                    <literal>cleanEclipseWtpFacet</literal>
+                </td>
+                <td>
+                    <literal>-</literal>
+                </td>
+                <td>
+                    <apilink class="org.gradle.api.tasks.Delete"/>
+                </td>
+                <td>Removes the <filename>.settings/org.eclipse.wst.common.component</filename> file.
+                </td>
             </tr>
             <tr>
                 <td>
@@ -138,8 +155,8 @@
                 <td>
                     <literal>-</literal>
                 </td>
-                <td><apilink class="org.gradle.plugins.eclipse.EclipseProject"/></td>
-                <td>Generates the eclipse project file.</td>
+                <td><apilink class="org.gradle.plugins.ide.eclipse.GenerateEclipseProject"/></td>
+                <td>Generates the <filename>.project</filename> file.</td>
             </tr>
             <tr>
                 <td>
@@ -148,8 +165,8 @@
                 <td>
                     <literal>-</literal>
                 </td>
-                <td><apilink class="org.gradle.plugins.eclipse.EclipseClasspath"/></td>
-                <td>Generates the Eclipse classpath file.</td>
+                <td><apilink class="org.gradle.plugins.ide.eclipse.GenerateEclipseClasspath"/></td>
+                <td>Generates the <filename>.classpath</filename> file.</td>
             </tr>
             <tr>
                 <td>
@@ -158,18 +175,30 @@
                 <td>
                     <literal>-</literal>
                 </td>
-                <td><apilink class="org.gradle.plugins.eclipse.EclipseJdt"/></td>
-                <td>Generates the JDT settings file.</td>
+                <td><apilink class="org.gradle.plugins.ide.eclipse.GenerateEclipseJdt"/></td>
+                <td>Generates the <filename>.settings/org.eclipse.jdt.core.prefs</filename> file.</td>
             </tr>
             <tr>
                 <td>
-                    <literal>eclipseWtp</literal>
+                    <literal>eclipseWtpComponent</literal>
                 </td>
                 <td>
                     <literal>-</literal>
                 </td>
-                <td><apilink class="org.gradle.plugins.eclipse.EclipseWtp"/></td>
-                <td>Generates the WTP settings files.</td>
+                <td><apilink class="org.gradle.plugins.ide.eclipse.GenerateEclipseWtpComponent"/></td>
+                <td>Generates the <filename>.settings/org.eclipse.wst.common.component</filename> file.</td>
+            </tr>
+            <tr>
+                <td>
+                    <literal>eclipseWtpFacet</literal>
+                </td>
+                <td>
+                    <literal>-</literal>
+                </td>
+                <td>
+                    <apilink class="org.gradle.plugins.ide.eclipse.GenerateEclipseWtpFacet"/>
+                </td>
+                <td>Generates the <filename>.settings/org.eclipse.wst.common.project.facet.core.xml</filename> file.</td>
             </tr>
         </table>
 
@@ -201,7 +230,7 @@
                     <classname>String</classname>
                 </td>
                 <td><literal>project.description</literal></td>
-                <td>A comment for the eclipse project.</td>
+                <td>A comment for the Eclipse project.</td>
             </tr>
             <tr>
                 <td>
@@ -243,7 +272,7 @@
                     <classname>Set<Link></classname>
                 </td>
                 <td>empty set</td>
-                <td>The links for this Eclipse project.</td>
+                <td>The links for the Eclipse project.</td>
             </tr>
         </table>
 
@@ -263,9 +292,9 @@
                 </td>
                 <td><classname>Iterable<SourceSet></classname></td>
                 <td>
-                    <literal>sourceSets</literal>
+                    <literal>project.sourceSets</literal>
                 </td>
-                <td>The source sets which source directories should be added to the Eclipse classpath.</td>
+                <td>The source sets whose source directories are to be added to the Eclipse classpath.</td>
             </tr>
             <tr>
                 <td>
@@ -285,7 +314,7 @@
                     <classname>Set<Configuration></classname>
                 </td>
                 <td><literal>[configurations.testRuntime]</literal></td>
-                <td>The configurations, which files are to be transformed into classpath entries.</td>
+                <td>The configurations whose files are to be transformed into classpath entries.</td>
             </tr>
             <tr>
                 <td>
@@ -295,7 +324,7 @@
                     <classname>Set<Configuration></classname>
                 </td>
                 <td>empty set</td>
-                <td>The configurations which files are to be excluded from the classpath entries.</td>
+                <td>The configurations whose files are to be excluded from the classpath entries.</td>
             </tr>
             <tr>
                 <td>
@@ -307,7 +336,7 @@
                 <td>
                     <literal>true</literal>
                 </td>
-                <td>Whether to download sources for the external dependencies.</td>
+                <td>Whether to download sources for external dependencies.</td>
             </tr>
             <tr>
                 <td>
@@ -319,7 +348,7 @@
                 <td>
                     <literal>false</literal>
                 </td>
-                <td>Whether to download javadoc for the external dependencies.</td>
+                <td>Whether to download javadoc for external dependencies.</td>
             </tr>
             <tr>
                 <td>
@@ -334,8 +363,8 @@
             </tr>
         </table>
 
-        <table id='eclipse-wtp'>
-            <title>EclipseWtp task</title>
+        <table id='eclipse-wtp-component'>
+            <title>EclipseWtpComponent task</title>
             <thead>
                 <tr>
                     <td>Property</td>
@@ -346,13 +375,13 @@
             </thead>
             <tr>
                 <td>
-                    <literal>sourceSets</literal>
+                    <literal>sourceDirs</literal>
                 </td>
-                <td><classname>Iterable<SourceSet></classname></td>
+                <td><classname>Set<File></classname></td>
                 <td>
-                    <literal>project.sourceSets</literal>
+                    The source directories of <literal>sourceSets.main</literal>
                 </td>
-                <td>The source sets which source directories should be added to the Eclipse classpath.</td>
+                <td>The source sets whose source directories are to be added to the Eclipse classpath.</td>
             </tr>
             <tr>
                 <td>
@@ -362,7 +391,7 @@
                     <classname>String</classname>
                 </td>
                 <td><literal>project.name</literal></td>
-                <td>The deploy name to be used in the org.eclipse.wst.common.component file.</td>
+                <td>The deploy name to be used.</td>
             </tr>
             <tr>
                 <td>
@@ -372,7 +401,7 @@
                     <classname>Set<Configuration></classname>
                 </td>
                 <td><literal>[configurations.testRuntime]</literal></td>
-                <td>The configurations, which files are to be transformed into classpath entries.</td>
+                <td>The configurations whose files are to be transformed into classpath entries.</td>
             </tr>
             <tr>
                 <td>
@@ -382,7 +411,7 @@
                     <classname>Set<Configuration></classname>
                 </td>
                 <td><literal>[configurations.providedRuntime]</literal></td>
-                <td>The configurations which files are to be excluded from the classpath entries.</td>
+                <td>The configurations whose files are to be excluded from the classpath entries.</td>
             </tr>
             <tr>
                 <td>
@@ -395,105 +424,126 @@
                 <td>If the beginning of the absolute path of a library matches a value of a variable,
                 a variable entry is created. The matching part of the library path is replaced with the variable name.</td>
             </tr>
-            <tr>
-                <td>
-                    <literal>facets</literal>
-                </td>
-                <td>
-                    <classname>List<Facet></classname>
-                </td>
-                <td><literal>jst.java</literal> and <literal>jst.web</literal> facet</td>
-                <td>The facets to be added as installed elements to the org.eclipse.wst.common.project.facet.core file.</td>
-            </tr>
         </table>
 
-        <table id='eclipse-task-hooks'>
-            <title>Task Hooks</title>
+        <table id='eclipse-wtp-facet'>
+            <title>EclipseWtpFacet task</title>
             <thead>
                 <tr>
-                    <td>Method</td>
-                    <td>EclipseProject Task Argument</td>
-                    <td>EclipseClasspath Task Argument</td>
-                    <td>EclipseWtp Task Argument</td>
+                    <td>Property</td>
+                    <td>Type</td>
+                    <td>Default Value</td>
                     <td>Description</td>
                 </tr>
             </thead>
             <tr>
                 <td>
-                    <literal><code>beforeConfigured { arg -> }</code></literal>
+                    <literal>facets</literal>
                 </td>
-                <td><apilink class="org.gradle.plugins.eclipse.model.Project"/></td>
-                <td><apilink class="org.gradle.plugins.eclipse.model.Classpath"/></td>
-                <td><apilink class="org.gradle.plugins.eclipse.model.Wtp"/></td>
-                <td>Gets called directly after the domain objects have been populated with the content of the
-                    existing XML file (if there is one).</td>
-            </tr>
-            <tr>
                 <td>
-                    <literal><code>whenConfigured { arg -> }</code></literal>
+                    <classname>List<Facet></classname>
                 </td>
-                <td><apilink class="org.gradle.plugins.eclipse.model.Project"/></td>
-                <td><apilink class="org.gradle.plugins.eclipse.model.Classpath"/></td>
-                <td><apilink class="org.gradle.plugins.eclipse.model.Wtp"/></td>
-                <td>Gets called after the domain objects have been populated with the content of the
-                    existing XML file and the content from the build script.</td>
-            </tr>
-            <tr>
                 <td>
-                    <literal><code>withXml { arg -> }</code></literal>
+                    <literal>jst.java</literal>
+                    and
+                    <literal>jst.web</literal>
+                    facet
                 </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>
+                <td>The facets to be added as installed elements.</td>
             </tr>
         </table>
     </section>
     <section>
         <title>Customizing the generated files</title>
-        <para>All the tasks of the eclipse plugin provide the same hooks and behavior for customizing the generated content.</para>
-        <para>The tasks recognize existing eclipse files. If an eclipse project, classpath or wtp file does not exists,
-        default values are used. Otherwise the existing ones are merged.</para>
+        <para>All Eclipse tasks provide the same hooks and behavior for customizing the generated content.</para>
+        <para>The tasks recognize existing Eclipse files, and merge them with the generated content.</para>
         <section>
             <title>Merging</title>
-            <para>The first option to customize the Eclipse files is to have an existing Eclipse file before the tasks are run.
-            Existing files will be merged together with the generated content.
-            Any section of the existing Eclipse files that are not the target of generated content will neither be changed nor removed.</para>
+            <para>Sections of existing Eclipse files that are also the target of generated content will be amended or overwritten,
+                 depending on the particular section. The remaining sections will be left as-is.</para>
             <section id="sec:complete-overwrite">
                 <title>Disabling merging with a complete overwrite</title>
-                <para>If you want Gradle to completely overwrite existing Eclipse files you can specify this on the command line by
-                    executing something like <userinput>gradle cleanEclipse eclipse</userinput> or <userinput>gradle cleanEclipseClasspath eclipseClasspath</userinput>.
-                    You can specify this also in the build script. Just make the generating tasks depending on the deleting tasks. You can tailor this
-                    to your needs. You could make the <literal>eclipse</literal> task dependent on the <literal>cleanEclipse</literal> task. If you only want
-                    to overwrite for example the classpath files you simply make the <literal>eclipseClasspath</literal> task dependent on the
-                    <literal>cleanEclipseClasspath</literal> task.
+                <para>To completely overwrite existing Eclipse files, execute a clean task together with its corresponding generation task,
+                    for example <userinput>gradle cleanEclipse eclipse</userinput> (in that order). If you want to make this
+                    the default behavior, add <code>eclipse.dependsOn(cleanEclipse)</code> to your build script. This makes it
+                    unnecessary to execute the clean task explicitly.
+                </para>
+                <para>Complete overwrite works equally well for individual files, for example by executing<userinput>gradle cleanEclipseClasspath eclipseClasspath</userinput>.
                 </para>
             </section>
         </section>
         <section>
             <title>Hooking into the generation lifecycle</title>
-            <para>The Eclipse plugin provides a couple of domain classes that model the Eclipse files.
-                But only the sections that are autogenerated by Gradle. The generation lifecycle is the following.
-                If there is an existing Eclipse file, its whole XML is parsed and stored in memory. Then the domain
-                objects are populated with the relevant content of the the existing XML. After that the build script information
-                is used to further populate those objects (e.g. add additional dependencies).
-                Then all sections modelled by the domain objects are removed from the XML in memory. After that the domain objects are used to inject
-                their content into the XML. Finally the XML is written to disk. The lifecycle provides hooks to modify the result according to your needs.
+            <para>The Eclipse plugin provides domain classes modeling the sections of the Eclipse files
+                that are autogenerated by Gradle. The generation lifecycle is as follows:
+                <orderedlist>
+                    <listitem>If there is an existing file, its whole XML content is parsed and stored in memory; otherwise, a default file is used in its place</listitem>
+                    <listitem>The domain objects are populated with the relevant content of the existing file</listitem>
+                    <listitem>The <code>beforeConfigured</code> hook is executed</listitem>
+                    <listitem>The domain objects are populated with content from Gradle's build model, which may require merging with content from the existing file</listitem>
+                    <listitem>The <code>whenConfigured</code> hook is executed</listitem>
+                    <listitem>All sections modeled by the domain objects are removed from the in-memory XML representation</listitem>
+                    <listitem>The domain objects inject their content into the in-memory XML representation</listitem>
+                    <listitem>The <code>withXml</code> hook is executed</listitem>
+                    <listitem>The in-memory XML representation is written to disk</listitem>
+                </orderedlist>
+                The following table lists the domain object used for each of the Eclipse task types:
             </para>
+            <table id='eclipse-task-hooks'>
+                <title>Task Hooks</title>
+                <thead>
+                    <tr>
+                        <td>Task type</td>
+                        <td><literal>beforeConfigured { arg -> }</literal> argument type</td>
+                        <td><literal>whenConfigured { arg -> }</literal> argument type</td>
+                        <td><literal>withXml { arg -> }</literal> argument type</td>
+                    </tr>
+                </thead>
+                <tr>
+                    <td><apilink class="org.gradle.plugins.ide.eclipse.GenerateEclipseProject"/></td>
+                    <td><apilink class="org.gradle.plugins.ide.eclipse.model.Project"/></td>
+                    <td><apilink class="org.gradle.plugins.ide.eclipse.model.Project"/></td>
+                    <td><apilink class="org.gradle.api.artifacts.maven.XmlProvider"/></td>
+                </tr>
+                <tr>
+                    <td><apilink class="org.gradle.plugins.ide.eclipse.GenerateEclipseClasspath"/></td>
+                    <td><apilink class="org.gradle.plugins.ide.eclipse.model.Classpath"/></td>
+                    <td><apilink class="org.gradle.plugins.ide.eclipse.model.Classpath"/></td>
+                    <td><apilink class="org.gradle.api.artifacts.maven.XmlProvider"/></td>
+                </tr>
+                <tr>
+                    <td><apilink class="org.gradle.plugins.ide.eclipse.GenerateEclipseJdt"/></td>
+                    <td><apilink class="org.gradle.plugins.ide.eclipse.model.Jdt"/></td>
+                    <td><apilink class="org.gradle.plugins.ide.eclipse.model.Jdt"/></td>
+                    <td><apilink class="org.gradle.api.artifacts.maven.XmlProvider"/></td>
+                </tr>
+                <tr>
+                    <td><apilink class="org.gradle.plugins.ide.eclipse.GenerateEclipseWtpComponent"/></td>
+                    <td><apilink class="org.gradle.plugins.ide.eclipse.model.WtpComponent"/></td>
+                    <td><apilink class="org.gradle.plugins.ide.eclipse.model.WtpComponent"/></td>
+                    <td><apilink class="org.gradle.api.artifacts.maven.XmlProvider"/></td>
+                </tr>
+                <tr>
+                    <td><apilink class="org.gradle.plugins.ide.eclipse.GenerateEclipseWtpFacet"/></td>
+                    <td><apilink class="org.gradle.plugins.ide.eclipse.model.WtpFacet"/></td>
+                    <td><apilink class="org.gradle.plugins.ide.eclipse.model.WtpFacet"/></td>
+                    <td><apilink class="org.gradle.api.artifacts.maven.XmlProvider"/></td>
+                </tr>
+            </table>
             <section id="sec:partial-overwrite">
-                <title>Partial Overwrite</title>
-                <para>Doing a <link linkend="sec:complete-overwrite">complete overwrite</link> removes all your manual customizations.
-                    This might be not what you want.
-                    Therefore Gradle provides the option for a partial overwrite. You can hook into the phase just after the existing
-                    Eclipse files have populated the domain objects. You could then for example remove all the dependencies
-                    from the <literal>classpath</literal> object.
+                <title>Partial overwrite of existing content</title>
+                <para>A <link linkend="sec:complete-overwrite">complete overwrite</link> causes all existing content to be discarded,
+                    thereby losing any changes made directly in the IDE. The <code>beforeConfigured</code> hook makes it possible
+                    to overwrite just certain parts of the existing content. The following example removes all existing dependencies
+                    from the <literal>Classpath</literal> domain object:
                     <sample id="partialOverwrites" dir="eclipse"
-                            title="Partial Overwrite for Module">
+                            title="Partial Overwrite for Classpath">
                         <sourcefile file="build.gradle" snippet="module-before-configured"/>
                     </sample>
-                    The generated XML will have all sections of the existing Eclipse classpath file,
-                    except for the dependencies, where only the information of the build script is used. You could do something
-                    similar for the project file.
+                    The resulting <literal>.classpath</literal> file will only contain Gradle-generated dependency entries, but
+                    not any other dependency entries that may have been present in the original file. (In the case of dependency entries,
+                    this is also the default behavior.) Other sections of the <literal>.classpath</literal> file will be either left as-is or merged.
+                    The same could be done for the natures in the <literal>.project</literal> file:
                     <sample id="partialOverwritesProject" dir="eclipse"
                             title="Partial Overwrite for Project">
                         <sourcefile file="build.gradle" snippet="project-before-configured"/>
@@ -502,24 +552,23 @@
             </section>
             <section>
                 <title>Modifying the fully populated domain objects</title>
-                <para>You can also hook into the phase after the existing Eclipse files and the build script metadata have
-                    populated the domain objects. You could then for example disable export of all the dependencies
-                    of the <literal>module</literal> object.
+                <para>The <code>whenConfigured</code> hook allows to manipulate the fully populated domain objects. Often this is the
+                    preferred way to customize Eclipse files. Here is how you would export all the dependencies of an Eclipse project:
                     <sample id="exportDependencies" dir="eclipse"
                             title="Export Dependencies">
                         <sourcefile file="build.gradle" snippet="module-when-configured"/>
                     </sample>
-
                 </para>
             </section>
             <section>
-                <title>Modifying the XML</title>
-                <para>You can also hook into the phase after the XML DOM is fully populated but not written to disk.
-                    That hook provides total control over what is generated.
+                <title>Modifying the XML representation</title>
+                <para>The <code>withXml</code>hook allows to manipulate the in-memory XML representation just before the file gets written to disk.
+                    Although Groovy's XML support makes up for a lot, this approach is less convenient than manipulating the domain objects.
+                    In return, you get total control over the generated file, including sections not modeled by the domain objects.
                     <sample id="wtpWithXml" dir="eclipse"
                             title="Customizing the XML">
                         <sourcefile file="build.gradle" snippet="wtp-with-xml"/>
-                        <test args="eclipse"/>
+                        <test args="cleanEclipse eclipse"/>
                     </sample>
                 </para>
             </section>
diff --git a/subprojects/docs/src/docs/userguide/ideaPlugin.xml b/subprojects/docs/src/docs/userguide/ideaPlugin.xml
index fae0547..1153844 100644
--- a/subprojects/docs/src/docs/userguide/ideaPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/ideaPlugin.xml
@@ -15,15 +15,15 @@
   -->
 
 <chapter id="idea_plugin">
-    <title>The Idea Plugin</title>
+    <title>The IDEA Plugin</title>
 
-    <para>The Idea plugin generates files that are used by <ulink url="http://www.jetbrains.com/idea/">IntelliJ Idea IDE</ulink>, thus
-        making it possible to open the project from Idea (<guimenuitem>File</guimenuitem> - <guimenuitem>Open Project</guimenuitem>).
-        Both external (including associated source and javadoc files) and project dependencies are considered.</para>
+    <para>The IDEA plugin generates files that are used by <ulink url="http://www.jetbrains.com/idea/">IntelliJ IDEA</ulink>, thus
+        making it possible to open the project from IDEA (<guimenuitem>File</guimenuitem> - <guimenuitem>Open Project</guimenuitem>).
+        Both external dependencies (including associated source and javadoc files) and project dependencies are considered.</para>
 
-    <para>The Idea plugin will create different content depending on which other plugins being used:</para>
+    <para>What exactly the IDEA plugin generates depends on which other plugins are used:</para>
     <table>
-        <title>IDEA support for other plugins</title>
+        <title>IDEA plugin behavior</title>
         <thead>
             <tr>
                 <td>Plugin</td><td>Description</td>
@@ -37,29 +37,31 @@
             <td>
                 <link linkend="java_plugin">Java</link>
             </td>
-            <td>Adds java configuration to the module and project files.</td>
+            <td>Adds Java configuration to the module and project files.</td>
         </tr>
     </table>
 
-    <para>One focus of the Idea tasks is to be open to customizations. They provide a couple of hooks to add or remove
-        content from the generated files.
+    <para>One focus of the IDEA plugin is to be open to customization. Each task provides a standardized set of hooks
+        for adding and removing content from the generated files.
     </para>
+
     <section>
         <title>Usage</title>
-    <para>To use the Idea plugin, include in your build script:</para>
-    <sample id="useIdeaPlugin" dir="idea" title="Using the Idea plugin">
-        <sourcefile file="build.gradle" snippet="use-plugin"/>
-    </sample>
-        <para>The IDEA plugin adds a number of tasks to your project, as discussed below. The main task that you use is
-            the <literal>idea</literal> task.</para>
+        <para>To use the IDEA plugin, include this in your build script:</para>
+        <sample id="useIdeaPlugin" dir="idea" title="Using the IDEA plugin">
+            <sourcefile file="build.gradle" snippet="use-plugin"/>
+        </sample>
+        <para>The IDEA plugin adds a number of tasks to your project. The main tasks that you will use
+            are the <literal>idea</literal> and <literal>cleanIdea</literal> tasks.
+        </para>
     </section>
     <section>
         <title>Tasks</title>
 
-        <para>The Idea plugin adds the tasks shown below to a project.</para>
+        <para>The IDEA plugin adds the tasks shown below to a project.</para>
 
         <table id='ideatasks'>
-            <title>Idea plugin - Tasks</title>
+            <title>IDEA plugin - Tasks</title>
             <thead>
                 <tr>
                     <td>Task name</td>
@@ -74,7 +76,7 @@
                 </td>
                 <td><literal>ideaProject</literal>, <literal>ideaModule</literal>, <literal>ideaWorkspace</literal></td>
                 <td><literal>-</literal></td>
-                <td>Generates all the idea configuration files</td>
+                <td>Generates all IDEA configuration files</td>
             </tr>
             <tr>
                 <td>
@@ -84,7 +86,7 @@
                     <literal>cleanIdeaProject</literal>, <literal>cleanIdeaModule</literal>, <literal>cleanIdeaWorkspace</literal>
                 </td>
                 <td><apilink class="org.gradle.api.tasks.Delete"/></td>
-                <td>Removes all idea configuration files</td>
+                <td>Removes all IDEA configuration files</td>
             </tr>
             <tr>
                 <td>
@@ -94,7 +96,7 @@
                     <literal>-</literal>
                 </td>
                 <td><apilink class="org.gradle.api.tasks.Delete"/></td>
-                <td>Removes the idea project file</td>
+                <td>Removes the IDEA project file</td>
             </tr>
             <tr>
                 <td>
@@ -104,7 +106,7 @@
                     <literal>-</literal>
                 </td>
                 <td><apilink class="org.gradle.api.tasks.Delete"/></td>
-                <td>Removes the idea module file</td>
+                <td>Removes the IDEA module file</td>
             </tr>
             <tr>
                 <td>
@@ -114,7 +116,7 @@
                     <literal>-</literal>
                 </td>
                 <td><apilink class="org.gradle.api.tasks.Delete"/></td>
-                <td>Removes the idea workspace file</td>
+                <td>Removes the IDEA workspace file</td>
             </tr>
             <tr>
                 <td>
@@ -123,8 +125,8 @@
                 <td>
                     <literal>-</literal>
                 </td>
-                <td><apilink class="org.gradle.plugins.idea.IdeaProject"/></td>
-                <td>Generates the <filename>.ipr</filename> file. The Idea plugin adds this task only to the root project.</td>
+                <td><apilink class="org.gradle.plugins.ide.idea.GenerateIdeaProject"/></td>
+                <td>Generates the <literal>.ipr</literal> file. This task is only added to the root project.</td>
             </tr>
             <tr>
                 <td>
@@ -133,8 +135,8 @@
                 <td>
                     <literal>-</literal>
                 </td>
-                <td><apilink class="org.gradle.plugins.idea.IdeaModule"/></td>
-                <td>Generates the <filename>.iml</filename> file</td>
+                <td><apilink class="org.gradle.plugins.ide.idea.GenerateIdeaModule"/></td>
+                <td>Generates the <literal>.iml</literal> file</td>
             </tr>
             <tr>
                 <td>
@@ -143,8 +145,8 @@
                 <td>
                     <literal>-</literal>
                 </td>
-                <td><apilink class="org.gradle.plugins.idea.IdeaWorkspace"/></td>
-                <td>Generates the <filename>.iws</filename> file</td>
+                <td><apilink class="org.gradle.plugins.ide.idea.GenerateIdeaWorkspace"/></td>
+                <td>Generates the <literal>.iws</literal> file. This task is only added to the root project.</td>
             </tr>
         </table>
 
@@ -179,7 +181,21 @@
                 </td>
                 <td><literal><replaceable>projectDir</replaceable>/<replaceable><project.name></replaceable>.iml</literal></td>
                 <td>-</td>
-                <td>The iml file. Used to look for existing files and as the target for generation. Must not be null.</td>
+                <td>The <filename>.iml</filename> file. Used to look for existing files and as the target for generation. Must not be null.</td>
+            </tr>
+            <tr>
+                <td>
+                    <literal>inheritOutputDirs</literal>
+                </td>
+                <td>
+                    <classname>Boolean</classname>
+                </td>
+                <td>null</td>
+                <td>null</td>
+                <td>If <literal>null</literal>, the value in the existing or default module file is used (the default file uses
+                    <literal>true</literal>). If <literal>true</literal>, the module output directories will be located below the
+                    project output directories. If <literal>false</literal>, the directories specified with the <literal>sourceDirs</literal>
+                    and <literal>testSourceDirs</literal> properties are used.</td>
             </tr>
             <tr>
                 <td>
@@ -189,8 +205,8 @@
                     <classname>Set<File></classname>
                 </td>
                 <td>empty set</td>
-                <td>The source dirs of <literal>sourceSets.main</literal></td>
-                <td>The dirs containing the production sources. Must not be null.</td>
+                <td>The source directories of <literal>sourceSets.main</literal></td>
+                <td>The directories containing the production sources. Must not be null.</td>
             </tr>
             <tr>
                 <td>
@@ -200,8 +216,8 @@
                     <classname>Set<File></classname>
                 </td>
                 <td>empty set</td>
-                <td>The source dirs of <literal>sourceSets.test</literal></td>
-                <td>The dirs containing the test sources. Must not be null.</td>
+                <td>The source directories of <literal>sourceSets.test</literal></td>
+                <td>The directories containing the test sources. Must not be null.</td>
             </tr>
             <tr>
                 <td>
@@ -212,7 +228,7 @@
                 </td>
                 <td>empty set</td>
                 <td>-</td>
-                <td>The dirs to be excluded by idea. Must not be null.</td>
+                <td>The directories to be excluded by IDEA. Must not be null.</td>
             </tr>
             <tr>
                 <td>
@@ -224,8 +240,8 @@
                 <td>
                     <literal>null</literal>
                 </td>
-                <td><literal>sourceSets.main.classesDir</literal></td>
-                <td>The idea output dir for the production sources. If null no entry for output dirs is created.</td>
+                <td><literal>null</literal></td>
+                <td>The IDEA output directory for the production sources. If null, no entry is created.</td>
             </tr>
             <tr>
                 <td>
@@ -237,8 +253,8 @@
                 <td>
                     <literal>null</literal>
                 </td>
-                <td><literal>sourceSets.test.classesDir</literal></td>
-                <td>The idea output dir for the test sources. If null no entry for test output dirs is created.</td>
+                <td><literal>null</literal></td>
+                <td>The IDEA output directory for the test sources. If null, no entry is created.</td>
             </tr>
             <tr>
                 <td>
@@ -251,9 +267,9 @@
                     <literal>null</literal>
                 </td>
                 <td>-</td>
-                <td>If this is null the value of the existing or default ipr XML (inherited) is used. If it is set
-                to <literal>inherited</literal>, the project SDK is used. Otherwise the SDK for the corresponding
-                value of java version is used for this module</td>
+                <td>If null, the value of the existing or default <literal>.ipr</literal> file is used (the default file uses
+                    <literal>inherited</literal>). If set to <literal>inherited</literal>, the project SDK is used. Otherwise,
+                    the SDK for the corresponding value of java version is used.</td>
             </tr>
             <tr>
                 <td>
@@ -266,7 +282,7 @@
                     <literal>true</literal>
                 </td>
                 <td>-</td>
-                <td>Whether to download and add source jars associated with the dependency jars.</td>
+                <td>Whether to download and add source Jars associated with the dependency Jars.</td>
             </tr>
             <tr>
                 <td>
@@ -279,7 +295,7 @@
                     <literal>false</literal>
                 </td>
                 <td>-</td>
-                <td>Whether to download and add javadoc jars associated with the dependency jars. </td>
+                <td>Whether to download and add javadoc Jars associated with the dependency Jars. </td>
             </tr>
             <tr>
                 <td>
@@ -292,8 +308,8 @@
                     <classname>[:]</classname>
                 </td>
                 <td>COMPILE(compile), RUNTIME(runtime - compile), TEST(testRuntime - runtime)</td>
-                <td>The keys of this map are the Idea scopes. Each key points to another map that has two keys, plus and minus.
-                    The values of those keys are sets of <link linkend="sub:configurations">Configuration</link> objects. The files of the
+                <td>The keys of this map are the IDEA scopes. Each key points to another map that has two keys, plus and minus.
+                    The values of those keys are collections of <link linkend="sub:configurations">Configuration</link> objects. The files of the
                     plus configurations are added minus the files from the minus configurations. </td>
                 </tr>
             <tr>
@@ -307,8 +323,8 @@
                     <literal>[:]</literal>
                 </td>
                 <td>-</td>
-                <td>The variables to be used for replacing absolute paths in the iml entries. For example, you might add
-                    a <literal>GRADLE_USER_HOME</literal> variable to point to the Gradle user home dir.</td>
+                <td>The variables to be used for replacing absolute paths in the .iml file. For example, you might add
+                    a <literal>GRADLE_USER_HOME</literal> variable to point to the Gradle user home directory.</td>
             </tr>
         </table>
      
@@ -332,8 +348,8 @@
                 </td>
                 <td><literal>rootProject.allprojects</literal></td>
                 <td>-</td>
-                <td>The subprojects that should be mapped to modules in the <literal>ipr</literal>
-                    file. The subprojects will only be mapped, if the Idea plugin has been
+                <td>The subprojects that should be mapped to modules in the <literal>.ipr</literal>
+                    file. The subprojects will only be mapped if the IDEA plugin has been
                     applied to them.</td>
             </tr>
             <tr>
@@ -345,7 +361,7 @@
                 </td>
                 <td><literal><replaceable>projectDir</replaceable>/<replaceable><project.name></replaceable>.ipr</literal></td>
                 <td>-</td>
-                <td>The ipr file. Used to look for existing files and as the target for generation. Must not be null.</td>
+                <td>The <literal>.ipr</literal> file. Used to look for existing files and as the target for generation. Must not be null.</td>
             </tr>
             <tr>
                 <td>
@@ -358,7 +374,7 @@
                     <literal>1.6</literal>
                 </td>
                 <td>-</td>
-                <td>The java version used for defining the project sdk.</td>
+                <td>The Java version used for defining the project SDK.</td>
             </tr>
             <tr>
                 <td>
@@ -395,101 +411,90 @@
                 </td>
                 <td><literal><replaceable>projectDir</replaceable>/<replaceable><project.name></replaceable>.iws</literal></td>
                 <td>-</td>
-                <td>The iws file. Used to look for existing files and as the target for generation. Must not be null.</td>
-            </tr>
-        </table>
-
-
-        <table id='idea-task-hooks'>
-            <title>Task Hooks</title>
-            <thead>
-                <tr>
-                    <td>Method</td>
-                    <td>Project Task Argument</td>
-                    <td>Module Task Argument</td>
-                    <td>Workspace Task Argument</td>
-                    <td>Description</td>
-                </tr>
-            </thead>
-            <tr>
-                <td>
-                    <literal><code>beforeConfigured { arg -> }</code></literal>
-                </td>
-                <td><apilink class="org.gradle.plugins.idea.model.Project"/></td>
-                <td><apilink class="org.gradle.plugins.idea.model.Module"/></td>
-                <td><apilink class="org.gradle.plugins.idea.model.Workspace"/></td>
-                <td>Gets called directly after the domain objects have been populated with the content of the
-                    existing XML file (if there is one).</td>
-            </tr>
-            <tr>
-                <td>
-                    <literal><code>whenConfigured { arg -> }</code></literal>
-                </td>
-                <td><apilink class="org.gradle.plugins.idea.model.Project"/></td>
-                <td><apilink class="org.gradle.plugins.idea.model.Module"/></td>
-                <td><apilink class="org.gradle.plugins.idea.model.Workspace"/></td>
-                <td>Gets called after the domain objects have been populated with the content of the
-                    existing XML file and the content from the build script.</td>
-            </tr>
-            <tr>
-                <td>
-                    <literal><code>withXml { arg -> }</code></literal>
-                </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>
+                <td>The <literal>.iws</literal> file. Used to look for existing files and as the target for generation. Must not be null.</td>
             </tr>
         </table>
     </section>
     <section>
         <title>Customizing the generated files</title>
-        <para>Both, the <literal>IdeaProject</literal> and the <literal>IdeaModule</literal> task provide the
-            same hooks and behavior for customizing the generated content. The <literal>IdeaWorkspace</literal> only provides a hook
-            for direct XML manipulation.</para>
-        <para>The plugin recognizes existing idea files. If an idea module, project or workspace file does not exists,
-        a default one is used. Otherwise the existing ones are merged.</para>
+        <para>All IDEA tasks provide the same hooks and behavior for customizing the generated content.
+            However, the workspace file can effectively only be manipulated via the <code>withXml</code> hook,
+            because its corresponding domain object is essentially empty.</para>
+        <para>The tasks recognize existing IDEA files, and merge them with the generated content.</para>
         <section>
             <title>Merging</title>
-            <para>The first option to customize the Idea files is to have an existing Idea <literal>ipr</literal> or
-            <literal>iml</literal> file before the tasks are run.
-            Existing files will be merged together with the generated content.
-            Any section of the existing Idea files that are not the target of generated content will neither be changed nor removed.</para>
+            <para>Sections of existing IDEA files that are also the target of generated content will be amended or overwritten,
+                depending on the particular section. The remaining sections will be left as-is.</para>
             <section id="sec:complete-overwrite">
                 <title>Disabling merging with a complete overwrite</title>
-                <para>If you want Gradle to completely overwrite existing Idea files you can specify this on the command line by
-                    executing something like <userinput>gradle cleanIdea idea</userinput> or <userinput>gradle cleanIdeaModule ideaModule</userinput>.
-                    You can specify this also in the build script. Just make the generating tasks depending on the deleting tasks. You can tailor this
-                    to your needs. You could make the <literal>idea</literal> task dependent on the <literal>cleanIdea</literal> task. If you only want
-                    to overwrite for example the module files you simply make the <literal>ideaModule</literal> task dependent on the
-                    <literal>cleanIdeaModule</literal> task.
+                <para>To completely overwrite existing IDEA files, execute a clean task together with its corresponding generation task,
+                    for example <userinput>gradle cleanIdea idea</userinput> (in that order). If you want to make this
+                    the default behavior, add <code>idea.dependsOn(cleanIdea)</code> to your build script. This makes it
+                    unnecessary to execute the clean task explicitly.
                 </para>
+                <para>Complete overwrite works equally well for individual files, for example by executing<userinput>gradle cleanIdeaModule ideaModule</userinput>.</para>
             </section>
         </section>
         <section>
             <title>Hooking into the generation lifecycle</title>
-            <para>The Idea plugin provides a couple of domain classes that model Idea project and module files.
-                But only the sections that are autogenerated by Gradle. The generation lifecycle is the following.
-                If there is an existing Idea file, its whole XML is parsed and stored in memory. Then the domain
-                objects are populated with the relevant content of the the existing XML. After that the build script information
-                is used to further populate those objects (e.g. add additional dependencies).
-                Then all sections modelled by the domain objects are removed from the XML in memory. After that the domain objects are used to inject
-                their content into the XML. Finally the XML is written to disk. The lifecycle provides hooks to modify the result according to your needs.
+            <para>The IDEA plugin provides domain classes modeling the sections of the IDEA files
+                that are autogenerated by Gradle. The generation lifecycle is as follows:
+                <orderedlist>
+                    <listitem>If there is an existing file, its whole XML content is parsed and stored in memory; otherwise, a default file is used in its place</listitem>
+                    <listitem>The domain objects are populated with the relevant content of the existing file</listitem>
+                    <listitem>The <code>beforeConfigured</code> hook is executed</listitem>
+                    <listitem>The domain objects are populated with content from Gradle's build model, which may require merging with content from the existing file</listitem>
+                    <listitem>The <code>whenConfigured</code> hook is executed</listitem>
+                    <listitem>All sections modeled by the domain objects are removed from the in-memory XML representation</listitem>
+                    <listitem>The domain objects inject their content into the in-memory XML representation</listitem>
+                    <listitem>The <code>withXml</code> hook is executed</listitem>
+                    <listitem>The in-memory XML representation is written to disk</listitem>
+                </orderedlist>
+                The following table lists the domain objects used for each of the IDEA task types:
             </para>
+            <table id='idea-task-hooks'>
+                <title>Task Hooks</title>
+                <thead>
+                    <tr>
+                        <td>Task type</td>
+                        <td><literal>beforeConfigured { arg -> }</literal> argument type</td>
+                        <td><literal>whenConfigured { arg -> }</literal> argument type</td>
+                        <td><literal>withXml { arg -> }</literal> argument type</td>
+                    </tr>
+                </thead>
+                <tr>
+                    <td><apilink class="org.gradle.plugins.ide.idea.GenerateIdeaProject"/></td>
+                    <td><apilink class="org.gradle.plugins.ide.idea.model.Project"/></td>
+                    <td><apilink class="org.gradle.plugins.ide.idea.model.Project"/></td>
+                    <td><apilink class="org.gradle.api.artifacts.maven.XmlProvider"/></td>
+                </tr>
+                <tr>
+                    <td><apilink class="org.gradle.plugins.ide.idea.GenerateIdeaModule"/></td>
+                    <td><apilink class="org.gradle.plugins.ide.idea.model.Module"/></td>
+                    <td><apilink class="org.gradle.plugins.ide.idea.model.Module"/></td>
+                    <td><apilink class="org.gradle.api.artifacts.maven.XmlProvider"/></td>
+                </tr>
+                <tr>
+                    <td><apilink class="org.gradle.plugins.ide.idea.GenerateIdeaWorkspace"/></td>
+                    <td><apilink class="org.gradle.plugins.ide.idea.model.Workspace"/></td>
+                    <td><apilink class="org.gradle.plugins.ide.idea.model.Workspace"/></td>
+                    <td><apilink class="org.gradle.api.artifacts.maven.XmlProvider"/></td>
+                </tr>
+            </table>
             <section id="sec:partial-overwrite">
-                <title>Partial Overwrite</title>
-                <para>Doing a <link linkend="sec:complete-overwrite">complete overwrite</link> removes all your manual customizations.
-                    This might be not what you want.
-                    Therefore Gradle provides the option for a partial overwrite. You can hook into the phase just after the existing
-                    Idea files have populated the domain objects. You could then for example remove all the dependencies
-                    from the <literal>module</literal> object.
+                <title>Partial overwrite of existing content</title>
+                <para>A <link linkend="sec:complete-overwrite">complete overwrite</link> causes all existing content to be discarded,
+                    thereby losing any changes made directly in the IDE. The <code>beforeConfigured</code> hook makes it possible
+                    to overwrite just certain parts of the existing content. The following example removes all existing dependencies
+                    from the <literal>Module</literal> domain object:
                     <sample id="partialOverwrites" dir="idea"
                             title="Partial Overwrite for Module">
                         <sourcefile file="build.gradle" snippet="module-before-configured"/>
                     </sample>
-                    The generated XML will have all sections of the existing Idea module file,
-                    except for the dependencies, where only the information of the build script is used. You could do something
-                    similar for the project file.
+                    The resulting module file will only contain Gradle-generated dependency entries, but
+                    not any other dependency entries that may have been present in the original file. (In the case of dependency entries,
+                    this is also the default behavior.) Other sections of the module file will be either left as-is or merged.
+                    The same could be done for the module paths in the project file:
                     <sample id="partialOverwritesProject" dir="idea"
                             title="Partial Overwrite for Project">
                         <sourcefile file="build.gradle" snippet="project-before-configured"/>
@@ -498,9 +503,8 @@
             </section>
             <section>
                 <title>Modifying the fully populated domain objects</title>
-                <para>You can also hook into the phase after the existing Idea files and the build script metadata have
-                    populated the domain objects. You could then for example export all the dependencies
-                    of the <literal>module</literal> object.
+                <para>The <code>whenConfigured</code> hook allows to manipulate the fully populated domain objects. Often this is the
+                    preferred way to customize IDEA files. Here is how you would export all the dependencies of an IDEA module:
                     <sample id="exportDependencies" dir="idea"
                             title="Export Dependencies">
                         <sourcefile file="build.gradle" snippet="module-when-configured"/>
@@ -509,9 +513,10 @@
                 </para>
             </section>
             <section>
-                <title>Modifying the XML</title>
-                <para>You can also hook into the phase after the XML DOM is fully populated but not written to disk.
-                    That hook provides total control over what is generated.
+                <title>Modifying the XML representation</title>
+                <para>The <code>withXml</code>hook allows to manipulate the in-memory XML representation just before the file gets written to disk.
+                    Although Groovy's XML support makes up for a lot, this approach is less convenient than manipulating the domain objects.
+                    In return, you get total control over the generated file, including sections not modeled by the domain objects.
                     <sample id="projectWithXml" dir="idea"
                             title="Customizing the XML">
                         <sourcefile file="build.gradle" snippet="project-with-xml"/>
@@ -523,8 +528,8 @@
     </section>
     <section>
         <title>Further things to consider</title>
-        <para>The paths of the dependencies in the generated Idea files are absolute. If you manually define a path variable,
-            pointing to the gradle dependency cache, Idea will automatically replace the absolute dependency paths with
+        <para>The paths of the dependencies in the generated IDEA files are absolute. If you manually define a path variable
+            pointing to the Gradle dependency cache, IDEA will automatically replace the absolute dependency paths with
             this path variable. If you use such a path variable, you need to tell the ideaModule task the name of this variable,
             so that it can do a proper merge without creating duplicates.</para>
     </section>
diff --git a/subprojects/docs/src/docs/userguide/installation.xml b/subprojects/docs/src/docs/userguide/installation.xml
index 1fdfdf7..b3345b5 100644
--- a/subprojects/docs/src/docs/userguide/installation.xml
+++ b/subprojects/docs/src/docs/userguide/installation.xml
@@ -52,7 +52,7 @@
     </listitem>
     <listitem>
         <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).
+            or checkout the sources from the source repository. See the <ulink url="website:build.html">Gradle web site</ulink> for details.
         </para>
     </listitem>
 </itemizedlist>
@@ -83,15 +83,15 @@ some zip front ends for Mac OS X don't restore the file permissions properly.
 
 <screen>
 ------------------------------------------------------------
-Gradle 0.9.2
+Gradle 1.0-milestone-3
 ------------------------------------------------------------
 
-Gradle build time: Sunday, 23 January 2011 01:34:21 PM EST
-Groovy: 1.7.6
-Ant: Apache Ant version 1.8.1 compiled on April 30 2010
+Gradle build time: Monday, 25 April 2011 5:24:40 PM EST
+Groovy: 1.7.10
+Ant: Apache Ant(TM) version 1.8.2 compiled on December 20 2010
 Ivy: 2.2.0
 JVM: 1.6.0_22 (Sun Microsystems Inc. 17.1-b03)
-OS: Linux 2.6.35-23-generic amd64
+OS: Linux 2.6.35-28-generic amd64
 </screen>
 
 </section>
diff --git a/subprojects/docs/src/docs/userguide/javaPlugin.xml b/subprojects/docs/src/docs/userguide/javaPlugin.xml
index 1b7725b..32f507e 100644
--- a/subprojects/docs/src/docs/userguide/javaPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/javaPlugin.xml
@@ -899,7 +899,7 @@
         <title>Javadoc</title>
         <para>The <literal>javadoc</literal> task is an instance of <apilink class="org.gradle.api.tasks.javadoc.Javadoc"/>.
             It supports the core javadoc options and the options of the standard doclet described in the
-            <ulink url='http://java.sun.com/j2se/1.5.0/docs/tooldocs/windows/javadoc.html#referenceguide'>reference documentation</ulink>
+            <ulink url='http://download.oracle.com/javase/1.5.0/docs/tooldocs/windows/javadoc.html#referenceguide'>reference documentation</ulink>
             of the Javadoc executable.
             For a complete list of supported Javadoc options consult the API documentation of the following classes:
             <apilink class="org.gradle.external.javadoc.CoreJavadocOptions"/> and <apilink class="org.gradle.external.javadoc.StandardJavadocDocletOptions"/>. 
diff --git a/subprojects/docs/src/docs/userguide/logging.xml b/subprojects/docs/src/docs/userguide/logging.xml
index 62b9276..a5ece50 100644
--- a/subprojects/docs/src/docs/userguide/logging.xml
+++ b/subprojects/docs/src/docs/userguide/logging.xml
@@ -118,7 +118,7 @@
             </tr>
             <tr>
                 <td>
-                    <literal>-f</literal>
+                    <literal>-S</literal>
                 </td>
                 <td>The full stacktraces are printed out.</td>
             </tr>
@@ -183,7 +183,7 @@
         <para>You can replace much of Gradle's logging UI with your own. You might do this, for example, if you want to
             customize the UI in some way - to log more or less information, or to change the formatting. You replace
             the logging using the <apilink class="org.gradle.api.invocation.Gradle" method="useLogger"/> method. This
-            is accessable from a build script, or an init script, or via the embedding API. Below is an example
+            is accessible from a build script, or an init script, or via the embedding API. Below is an example
             init script which changes how task execution and build completion is logged.
         </para>
         <sample id="custom_logging_ui" dir="userguide/initScripts/customLogger" title="Customizing what Gradle logs">
diff --git a/subprojects/docs/src/docs/userguide/mavenPlugin.xml b/subprojects/docs/src/docs/userguide/mavenPlugin.xml
index 2e46304..0505b1d 100644
--- a/subprojects/docs/src/docs/userguide/mavenPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/mavenPlugin.xml
@@ -300,7 +300,7 @@
             <title>Maven POM generation</title>
             <para>The Maven POMs for uploading are automatically generated by Gradle. The groupId, artifactId, version and packaging
                 values are taken from the project object. The dependency elements are created from the Gradle dependency declarations.
-                the You can find the generated POMs in the directory
+                You can find the generated POMs in the directory
                 <literal><buildDir>/poms</literal>. You can further customize the POM via the API of the
                 <apilink class="org.gradle.api.artifacts.maven.MavenPom"/> object.
             </para>
diff --git a/subprojects/docs/src/docs/userguide/scalaPlugin.xml b/subprojects/docs/src/docs/userguide/scalaPlugin.xml
index 97e87eb..a4dddf1 100644
--- a/subprojects/docs/src/docs/userguide/scalaPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/scalaPlugin.xml
@@ -258,4 +258,18 @@
         </table>
     </section>
 
+    <section>
+        <title>Fast Scala Compiler</title>
+        <para>The Scala plugin includes support for <ulink url="http://www.scala-lang.org/docu/files/tools/fsc.html">fsc</ulink>,
+            the Fast Scala Compiler. <literal>fsc</literal> runs in a separate daemon process and can speed up
+            compilation significantly.
+            <sample id="fsc" dir="scala/fsc" title="Enabling the Fast Scala Compiler">
+                <sourcefile file="build.gradle" snippet="use-fsc"/>
+            </sample>
+            Note that <literal>fsc</literal> expects to be restarted whenever the <emphasis>contents</emphasis> of its
+            compile class path change. (It does detect changes to the compile class path itself.) This makes it
+            less suitable for multi-project builds.
+        </para>
+    </section>
+
 </chapter>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/userguide/sonarPlugin.xml b/subprojects/docs/src/docs/userguide/sonarPlugin.xml
new file mode 100644
index 0000000..460704e
--- /dev/null
+++ b/subprojects/docs/src/docs/userguide/sonarPlugin.xml
@@ -0,0 +1,83 @@
+<!--
+  ~ Copyright 2011 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<chapter id="sonar_plugin">
+    <title>The Sonar Plugin</title>
+
+    <para>The Sonar plugin provides integration with <ulink url="http://www.sonarsource.org">Sonar</ulink>,
+        a web-based platform for monitoring code quality. The plugin adds a <literal>sonar</literal> task
+        to the project, which analyzes the project's source code and stores the results in Sonar's database.
+    </para>
+
+    <para>
+        The <literal>sonar</literal> task is a standalone task that needs to be executed explicitly. By default,
+        it gets configured to analyze the Java code in the main source set. In a typical setup, the task would
+        be run once per day on a build server.
+    </para>
+
+    <para>
+        Only projects which have the Java plugin applied (possibly by way of another plugin) are affected by the
+        Sonar plugin. Other projects can still declare a task of type <apilink class="org.gradle.api.plugins.sonar.Sonar"/>
+        and configure it manually.
+    </para>
+
+    <section>
+        <title>Usage</title>
+        <para>At a minimum, the Sonar plugin has to be applied to the project.</para>
+        <sample id="useSonarPlugin" dir="sonar" title="Using the Sonar plugin">
+            <sourcefile file="build.gradle" snippet="use-plugin"/>
+        </sample>
+        <para>Typically, it is also necessary to configure connection settings for the Sonar server and database.</para>
+        <sample id="useSonarPlugin" dir="sonar" title="Configuring connection settings">
+            <sourcefile file="build.gradle" snippet="connection-settings"/>
+        </sample>
+        <para>For a complete documentation of all Sonar-specific configuration properties, see the
+            <ulink url= "http://docs.codehaus.org/display/SONAR/Advanced+parameters">Sonar documentation</ulink>.
+        </para>
+    </section>
+
+    <section>
+        <title>Tasks</title>
+        <para>The Sonar plugin adds the following tasks to the project.</para>
+        <table>
+            <title>Sonar plugin - tasks</title>
+            <thead>
+                <tr>
+                    <td>Task name</td>
+                    <td>Depends on</td>
+                    <td>Type</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+            <tr>
+                <td><literal>sonar</literal></td>
+                <td>-</td>
+                <td><apilink class="org.gradle.api.plugins.sonar.Sonar"/></td>
+                <td>Analyzes the project's source code and stores results in Sonar's database.</td>
+            </tr>
+        </table>
+    </section>
+
+    <section>
+        <title>Limitations</title>
+        <itemizedlist>
+            <listitem>
+                <para>The projects of a multi-project build are currently analyzed independently. This means that no
+                    aggregated view will be available.
+                </para>
+            </listitem>
+        </itemizedlist>
+    </section>
+</chapter>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/userguide/standardPlugins.xml b/subprojects/docs/src/docs/userguide/standardPlugins.xml
index d0b6800..67bc013 100644
--- a/subprojects/docs/src/docs/userguide/standardPlugins.xml
+++ b/subprojects/docs/src/docs/userguide/standardPlugins.xml
@@ -99,6 +99,17 @@
             </tr>
             <tr>
                 <td>
+                    <link linkend='application_plugin'><literal>application</literal></link>
+                </td>
+                <td><literal>-</literal></td>
+                <td><literal>java</literal>, <literal>groovy</literal></td>
+                <td>
+                    <para>Adds tasks for running and bundling a project as application.
+                    </para>
+                </td>
+            </tr>
+            <tr>
+                <td>
                     <link linkend='jetty_plugin'><literal>jetty</literal></link>
                 </td>
                 <td><literal>war</literal></td>
@@ -198,6 +209,17 @@
                     </para>
                 </td>
             </tr>
+            <tr>
+                <td>
+                    <link linkend='sonar_plugin'> <literal>sonar</literal> </link>
+                </td>
+                <td>-</td>
+                <td>-</td>
+                <td>
+                    <para>Provides integration with the Sonar code quality platform.
+                    </para>
+                </td>
+            </tr>
         </table>
     </section>
     <section>
diff --git a/subprojects/docs/src/docs/userguide/userguide.xml b/subprojects/docs/src/docs/userguide/userguide.xml
index d185dda..7efc0cc 100644
--- a/subprojects/docs/src/docs/userguide/userguide.xml
+++ b/subprojects/docs/src/docs/userguide/userguide.xml
@@ -61,12 +61,14 @@
     <xi:include href='warPlugin.xml'/>
     <xi:include href='jettyPlugin.xml'/>
     <xi:include href='codeQualityPlugin.xml'/>
+    <xi:include href='sonarPlugin.xml'/>
     <xi:include href='osgi.xml'/>
     <xi:include href='eclipsePlugin.xml'/>
     <xi:include href='ideaPlugin.xml'/>
     <xi:include href='antlrPlugin.xml'/>
     <xi:include href='projectReports.xml'/>
     <xi:include href='announcePlugin.xml'/>
+    <xi:include href='applicationPlugin.xml'/>
 	<xi:include href='depMngmt.xml'/>
     <xi:include href='artifactMngmt.xml'/>
     <xi:include href='mavenPlugin.xml'/>
diff --git a/subprojects/docs/src/docs/userguide/workingWithFiles.xml b/subprojects/docs/src/docs/userguide/workingWithFiles.xml
index fe364a4..2e27bf9 100644
--- a/subprojects/docs/src/docs/userguide/workingWithFiles.xml
+++ b/subprojects/docs/src/docs/userguide/workingWithFiles.xml
@@ -58,7 +58,7 @@
             objects, which are then converted into a set of <classname>File</classname> objects. The
             <literal>files()</literal> method accepts any type of object as its parameters. These are evaluated relative
             to the project directory, as for the <literal>file()</literal> method, described in <xref linkend="sec:locating_files"/>.
-            You can also pass collections, maps and arrays to the <literal>files()</literal> method. These are flattened
+            You can also pass collections, iterables, maps and arrays to the <literal>files()</literal> method. These are flattened
             and the contents converted to <classname>File</classname> instances.
         </para>
 
diff --git a/subprojects/docs/src/samples/antlr/build.gradle b/subprojects/docs/src/samples/antlr/build.gradle
index eedca0a..fb5f522 100644
--- a/subprojects/docs/src/samples/antlr/build.gradle
+++ b/subprojects/docs/src/samples/antlr/build.gradle
@@ -10,7 +10,7 @@ repositories {
 dependencies {
     antlr 'antlr:antlr:2.7.7'
 // END SNIPPET declare-dependency
-    testCompile 'junit:junit:4.7'
+    testCompile 'junit:junit:4.8.2'
 // START SNIPPET declare-dependency
 }
 // END SNIPPET declare-dependency
diff --git a/subprojects/docs/src/samples/application/build.gradle b/subprojects/docs/src/samples/application/build.gradle
new file mode 100644
index 0000000..6d5f37e
--- /dev/null
+++ b/subprojects/docs/src/samples/application/build.gradle
@@ -0,0 +1,18 @@
+apply plugin:'java'
+// START SNIPPET use-plugin
+apply plugin:'application'
+// END SNIPPET use-plugin
+
+version = '1.0.2'
+
+// START SNIPPET mainClassName-conf
+mainClassName = "org.gradle.sample.Main"
+// END SNIPPET mainClassName-conf
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    compile 'commons-collections:commons-collections:3.2.1'
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/application/readme.xml b/subprojects/docs/src/samples/application/readme.xml
new file mode 100644
index 0000000..8cfe8e0
--- /dev/null
+++ b/subprojects/docs/src/samples/application/readme.xml
@@ -0,0 +1,3 @@
+<sample>
+    <para>A project which uses the application plugin</para>
+</sample>
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/application/src/main/java/org/gradle/sample/Main.java b/subprojects/docs/src/samples/application/src/main/java/org/gradle/sample/Main.java
new file mode 100644
index 0000000..315288a
--- /dev/null
+++ b/subprojects/docs/src/samples/application/src/main/java/org/gradle/sample/Main.java
@@ -0,0 +1,9 @@
+package org.gradle.sample;
+
+import org.apache.commons.collections.list.GrowthList;
+public class Main {
+    public static void main(String[] args) {
+        GrowthList l = new GrowthList();
+        System.out.println("Greetings from the sample application.");
+    }
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/clientModuleDependencies/build.gradle b/subprojects/docs/src/samples/clientModuleDependencies/build.gradle
index 0f6aeb1..23ce57a 100644
--- a/subprojects/docs/src/samples/clientModuleDependencies/build.gradle
+++ b/subprojects/docs/src/samples/clientModuleDependencies/build.gradle
@@ -4,7 +4,7 @@ subprojects {
             mavenCentral()
         }
         dependencies {
-            classpath 'junit:junit:4.7'
+            classpath 'junit:junit:4.8.2'
         }
     }
     apply plugin: 'java'
diff --git a/subprojects/docs/src/samples/codeQuality/build.gradle b/subprojects/docs/src/samples/codeQuality/build.gradle
index 54f1631..700266a 100644
--- a/subprojects/docs/src/samples/codeQuality/build.gradle
+++ b/subprojects/docs/src/samples/codeQuality/build.gradle
@@ -1,13 +1,13 @@
-// START SNIPPET use-plugin
-apply plugin: 'code-quality'
-// END SNIPPET use-plugin
-apply plugin: 'groovy'
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    groovy group: 'org.codehaus.groovy', name: 'groovy', version: '1.7.6'
-    testCompile group: 'junit', name: 'junit', version: '4.7'
+// START SNIPPET use-plugin
+apply plugin: 'code-quality'
+// END SNIPPET use-plugin
+apply plugin: 'groovy'
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    groovy group: 'org.codehaus.groovy', name: 'groovy', version: '1.7.10'
+    testCompile group: 'junit', name: 'junit', version: '4.8.2'
 }
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/customPlugin/build.gradle b/subprojects/docs/src/samples/customPlugin/build.gradle
index 9b93b92..14a47f3 100644
--- a/subprojects/docs/src/samples/customPlugin/build.gradle
+++ b/subprojects/docs/src/samples/customPlugin/build.gradle
@@ -14,7 +14,7 @@ repositories {
 
 dependencies {
     groovy localGroovy()
-    testCompile 'junit:junit:4.7'
+    testCompile 'junit:junit:4.8.2'
 }
 
 group = 'org.gradle'
diff --git a/subprojects/docs/src/samples/eclipse/build.gradle b/subprojects/docs/src/samples/eclipse/build.gradle
index d0fa226..0abdb06 100644
--- a/subprojects/docs/src/samples/eclipse/build.gradle
+++ b/subprojects/docs/src/samples/eclipse/build.gradle
@@ -28,9 +28,9 @@ eclipseProject {
 // END SNIPPET project-before-configured
 
 // START SNIPPET wtp-with-xml
-eclipseWtp {
-    withXml { files ->
-        files.'org.eclipse.wst.commons.project.facet.core'.fixed.find { it. at facet == 'jst.java' }. at facet = 'jst2.java'
+eclipseWtpFacet {
+    withXml { provider ->
+        provider.asNode().fixed.find { it. at facet == 'jst.java' }. at facet = 'jst2.java'
     }
 }
 // END SNIPPET wtp-with-xml
diff --git a/subprojects/docs/src/samples/groovy/customizedLayout/build.gradle b/subprojects/docs/src/samples/groovy/customizedLayout/build.gradle
index 7f14af5..6f7ba78 100644
--- a/subprojects/docs/src/samples/groovy/customizedLayout/build.gradle
+++ b/subprojects/docs/src/samples/groovy/customizedLayout/build.gradle
@@ -5,8 +5,8 @@ repositories {
 }
 
 dependencies {
-    groovy group: 'org.codehaus.groovy', name: 'groovy-all', version: '1.7.6'
-    testCompile group: 'junit', name: 'junit', version: '4.7'
+    groovy group: 'org.codehaus.groovy', name: 'groovy-all', version: '1.7.10'
+    testCompile group: 'junit', name: 'junit', version: '4.8.2'
 }
 
 // START SNIPPET define-main
diff --git a/subprojects/docs/src/samples/groovy/groovy-1.5.6/build.gradle b/subprojects/docs/src/samples/groovy/groovy-1.5.6/build.gradle
index e098600..de4094d 100644
--- a/subprojects/docs/src/samples/groovy/groovy-1.5.6/build.gradle
+++ b/subprojects/docs/src/samples/groovy/groovy-1.5.6/build.gradle
@@ -6,5 +6,5 @@ repositories {
 
 dependencies {
     groovy 'org.codehaus.groovy:groovy-all:1.5.6'
-    testCompile 'junit:junit:4.7'
+    testCompile 'junit:junit:4.8.2'
 }
diff --git a/subprojects/docs/src/samples/groovy/groovy-1.6.7/build.gradle b/subprojects/docs/src/samples/groovy/groovy-1.6.7/build.gradle
index d9a3bf4..fe926bf 100644
--- a/subprojects/docs/src/samples/groovy/groovy-1.6.7/build.gradle
+++ b/subprojects/docs/src/samples/groovy/groovy-1.6.7/build.gradle
@@ -6,5 +6,5 @@ repositories {
 
 dependencies {
     groovy 'org.codehaus.groovy:groovy-all:1.6.7'
-    testCompile 'junit:junit:4.7'
+    testCompile 'junit:junit:4.8.2'
 }
diff --git a/subprojects/docs/src/samples/groovy/mixedJavaAndGroovy/build.gradle b/subprojects/docs/src/samples/groovy/mixedJavaAndGroovy/build.gradle
index 6402f6a..aeb9e20 100644
--- a/subprojects/docs/src/samples/groovy/mixedJavaAndGroovy/build.gradle
+++ b/subprojects/docs/src/samples/groovy/mixedJavaAndGroovy/build.gradle
@@ -6,6 +6,6 @@ repositories {
 }
 
 dependencies {
-    groovy group: 'org.codehaus.groovy', name: 'groovy-all', version: '1.7.6'
-    testCompile group: 'junit', name: 'junit', version: '4.7'
+    groovy group: 'org.codehaus.groovy', name: 'groovy-all', version: '1.7.10'
+    testCompile group: 'junit', name: 'junit', version: '4.8.2'
 }
diff --git a/subprojects/docs/src/samples/groovy/multiproject/testproject/build.gradle b/subprojects/docs/src/samples/groovy/multiproject/testproject/build.gradle
index e336f79..1534fc4 100644
--- a/subprojects/docs/src/samples/groovy/multiproject/testproject/build.gradle
+++ b/subprojects/docs/src/samples/groovy/multiproject/testproject/build.gradle
@@ -4,9 +4,9 @@ group = 'org.gradle'
 version = '1.0'
 
 dependencies {
-    groovy 'org.codehaus.groovy:groovy-all:1.7.6'
+    groovy 'org.codehaus.groovy:groovy-all:1.7.10'
     compile project(':groovycDetector')
-    testCompile 'junit:junit:4.7'
+    testCompile 'junit:junit:4.8.2'
 }
 
 sourceSets {
diff --git a/subprojects/docs/src/samples/groovy/multiproject/testproject/src/test/groovy/org/gradle/VersionTest.groovy b/subprojects/docs/src/samples/groovy/multiproject/testproject/src/test/groovy/org/gradle/VersionTest.groovy
index 9b42126..99247e9 100644
--- a/subprojects/docs/src/samples/groovy/multiproject/testproject/src/test/groovy/org/gradle/VersionTest.groovy
+++ b/subprojects/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_6() {
-    assertEquals("1.7.6", groovycVersion)
+  void versionShouldBe1_7_10() {
+    assertEquals("1.7.10", groovycVersion)
   }
 }
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/groovy/quickstart/build.gradle b/subprojects/docs/src/samples/groovy/quickstart/build.gradle
index 3e91669..dee9f6f 100644
--- a/subprojects/docs/src/samples/groovy/quickstart/build.gradle
+++ b/subprojects/docs/src/samples/groovy/quickstart/build.gradle
@@ -9,9 +9,9 @@ repositories {
 }
 
 dependencies {
-    groovy group: 'org.codehaus.groovy', name: 'groovy', version: '1.7.6'
+    groovy group: 'org.codehaus.groovy', name: 'groovy', version: '1.7.10'
 // END SNIPPET groovy-dependency
-    testCompile group: 'junit', name: 'junit', version: '4.7'
+    testCompile group: 'junit', name: 'junit', version: '4.8.2'
 // START SNIPPET groovy-dependency
 }
 // END SNIPPET groovy-dependency
diff --git a/subprojects/docs/src/samples/groovy/quickstart/src/test/groovy/org/gradle/PersonTest.groovy b/subprojects/docs/src/samples/groovy/quickstart/src/test/groovy/org/gradle/PersonTest.groovy
index ad562e6..2a6c78d 100644
--- a/subprojects/docs/src/samples/groovy/quickstart/src/test/groovy/org/gradle/PersonTest.groovy
+++ b/subprojects/docs/src/samples/groovy/quickstart/src/test/groovy/org/gradle/PersonTest.groovy
@@ -1,6 +1,5 @@
 package org.gradle
 
-import org.codehaus.groovy.runtime.InvokerHelper
 import org.junit.Test
 import static org.junit.Assert.*
 
@@ -16,7 +15,7 @@ class PersonTest {
     }
 
     @Test public void usingCorrectVersionOfGroovy() {
-        assertEquals('1.7.6', InvokerHelper.version)
+        assertEquals('1.7.10', GroovySystem.version)
     }
     
     @Test public void testResourcesAreAvailable() {
diff --git a/subprojects/docs/src/samples/ivypublish/build.gradle b/subprojects/docs/src/samples/ivypublish/build.gradle
index 0e6caf9..c376953 100644
--- a/subprojects/docs/src/samples/ivypublish/build.gradle
+++ b/subprojects/docs/src/samples/ivypublish/build.gradle
@@ -7,7 +7,7 @@ version = '1.0'
 group = 'org.gradle.test'
 
 dependencies {
-   compile 'junit:junit:4.7', project(':subproject')
+   compile 'junit:junit:4.8.2', project(':subproject')
 }
 
 ant {
@@ -54,7 +54,7 @@ uploadArchives {
         def root = new XmlParser().parse(new File(repoDir, 'ivy.xml'))
         assert root.publications.artifact.find { it. at name == 'ivypublishSource' }.attribute(ns.classifier) == 'src'
         assert (root.configurations.conf.collect { it. at name } as Set) == ['archives', 'compile', 'default', 'runtime','testCompile', 'testRuntime']  as Set
-        assert root.dependencies.dependency.find { it. at org == 'junit' }.attributes() == [org: 'junit', name: 'junit', rev: '4.7', conf: 'compile->default']
+        assert root.dependencies.dependency.find { it. at org == 'junit' }.attributes() == [org: 'junit', name: 'junit', rev: '4.8.2', conf: 'compile->default']
         assert root.dependencies.dependency.find { it. at org == 'ivypublish' }.attributes() == [org: 'ivypublish', name: 'subproject', rev: 'unspecified',
                 conf: 'compile->default']
     }
diff --git a/subprojects/docs/src/samples/java/base/test/build.gradle b/subprojects/docs/src/samples/java/base/test/build.gradle
index b253b27..11e970f 100644
--- a/subprojects/docs/src/samples/java/base/test/build.gradle
+++ b/subprojects/docs/src/samples/java/base/test/build.gradle
@@ -1,5 +1,5 @@
 dependencies {
-    compile group: 'junit', name: 'junit', version: '4.7', project(':prod')
+    compile group: 'junit', name: 'junit', version: '4.8.2', project(':prod')
 }
 
 task test(type: Test) {
diff --git a/subprojects/docs/src/samples/java/customizedLayout/build.gradle b/subprojects/docs/src/samples/java/customizedLayout/build.gradle
index 9214ff3..c606bcf 100644
--- a/subprojects/docs/src/samples/java/customizedLayout/build.gradle
+++ b/subprojects/docs/src/samples/java/customizedLayout/build.gradle
@@ -5,7 +5,7 @@ repositories {
 }
 
 dependencies {
-    testCompile group: 'junit', name: 'junit', version: '4.7'
+    testCompile group: 'junit', name: 'junit', version: '4.8.2'
 }
 
 // START SNIPPET define-main
diff --git a/subprojects/docs/src/samples/java/multiproject/build.gradle b/subprojects/docs/src/samples/java/multiproject/build.gradle
index 55391fe..51a8299 100644
--- a/subprojects/docs/src/samples/java/multiproject/build.gradle
+++ b/subprojects/docs/src/samples/java/multiproject/build.gradle
@@ -8,7 +8,7 @@ subprojects {
     }
 
     dependencies {
-        testCompile 'junit:junit:4.7'
+        testCompile 'junit:junit:4.8.2'
     }
 
     version = '1.0'
diff --git a/subprojects/docs/src/samples/java/multiproject/buildSrc/build.gradle b/subprojects/docs/src/samples/java/multiproject/buildSrc/build.gradle
index f8baabe..ad3506c 100644
--- a/subprojects/docs/src/samples/java/multiproject/buildSrc/build.gradle
+++ b/subprojects/docs/src/samples/java/multiproject/buildSrc/build.gradle
@@ -8,7 +8,7 @@ repositories {
 dependencies {
     compile gradleApi()
 // END SNIPPET gradle-api-dependencies
-    testCompile group: 'junit', name: 'junit', version: '4.7'
+    testCompile group: 'junit', name: 'junit', version: '4.8.2'
 // START SNIPPET gradle-api-dependencies
 }
 // END SNIPPET gradle-api-dependencies
diff --git a/subprojects/docs/src/samples/java/onlyif/build.gradle b/subprojects/docs/src/samples/java/onlyif/build.gradle
index a5b141e..be33abb 100644
--- a/subprojects/docs/src/samples/java/onlyif/build.gradle
+++ b/subprojects/docs/src/samples/java/onlyif/build.gradle
@@ -5,7 +5,7 @@ repositories {
 }
 
 dependencies {
-    testCompile group: 'junit', name: 'junit', version: '4.7'
+    testCompile group: 'junit', name: 'junit', version: '4.8.2'
 }
 
 test {
diff --git a/subprojects/docs/src/samples/java/quickstart/src/main/resources/org/gradle/resource.xml b/subprojects/docs/src/samples/java/quickstart/src/main/resources/org/gradle/resource.xml
new file mode 100644
index 0000000..b5db07b
--- /dev/null
+++ b/subprojects/docs/src/samples/java/quickstart/src/main/resources/org/gradle/resource.xml
@@ -0,0 +1 @@
+<some-element/>
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/java/quickstart/src/test/resources/org/gradle/test-resource.xml b/subprojects/docs/src/samples/java/quickstart/src/test/resources/org/gradle/test-resource.xml
new file mode 100644
index 0000000..5c933c7
--- /dev/null
+++ b/subprojects/docs/src/samples/java/quickstart/src/test/resources/org/gradle/test-resource.xml
@@ -0,0 +1 @@
+<some-resource/>
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/java/withIntegrationTests/build.gradle b/subprojects/docs/src/samples/java/withIntegrationTests/build.gradle
index b8b7285..a6d81a8 100644
--- a/subprojects/docs/src/samples/java/withIntegrationTests/build.gradle
+++ b/subprojects/docs/src/samples/java/withIntegrationTests/build.gradle
@@ -14,7 +14,7 @@ configurations {
 }
 
 dependencies {
-    testCompile group: 'junit', name: 'junit', version: '4.7'
+    testCompile group: 'junit', name: 'junit', version: '4.8.2'
     integrationTestCompile group: 'commons-collections', name: 'commons-collections', version: '3.2'
 }
 
diff --git a/subprojects/docs/src/samples/maven/pomGeneration/build.gradle b/subprojects/docs/src/samples/maven/pomGeneration/build.gradle
index 5b16955..3328ca2 100644
--- a/subprojects/docs/src/samples/maven/pomGeneration/build.gradle
+++ b/subprojects/docs/src/samples/maven/pomGeneration/build.gradle
@@ -1,5 +1,3 @@
-import org.apache.maven.settings.Settings
-
 apply plugin: 'war'
 // START SNIPPET use-plugin
 apply plugin: 'maven'
@@ -99,7 +97,7 @@ task writeDeployerPom(dependsOn: uploadArchives) << {
 // For our integration tests
 
 install.doLast {
-    Settings settings = installer.settings
+    def settings = installer.settings
     new File(buildDir, "localRepoPath.txt").write(settings.localRepository)
 }
 
diff --git a/subprojects/docs/src/samples/maven/quickstart/build.gradle b/subprojects/docs/src/samples/maven/quickstart/build.gradle
index e2f966a..afe7d51 100644
--- a/subprojects/docs/src/samples/maven/quickstart/build.gradle
+++ b/subprojects/docs/src/samples/maven/quickstart/build.gradle
@@ -1,4 +1,3 @@
-import org.apache.maven.settings.Settings
 
 apply plugin: 'java'
 // START SNIPPET use-plugin
@@ -21,7 +20,7 @@ uploadArchives {
 // For our integration tests
 
 install.doLast {
-    Settings settings = repositories.mavenInstaller.settings
+    def settings = repositories.mavenInstaller.settings
     new File(buildDir, "localRepoPath.txt").write(settings.localRepository)
 }
 
diff --git a/subprojects/docs/src/samples/osgi/build.gradle b/subprojects/docs/src/samples/osgi/build.gradle
index 62f2367..57a65f8 100644
--- a/subprojects/docs/src/samples/osgi/build.gradle
+++ b/subprojects/docs/src/samples/osgi/build.gradle
@@ -12,7 +12,7 @@ repositories {
 }
 
 dependencies {
-    groovy group: 'org.codehaus.groovy', name: 'groovy-all', version: '1.7.6'
+    groovy group: 'org.codehaus.groovy', name: 'groovy-all', version: '1.7.10'
     compile group: 'org.eclipse', name: 'osgi', version: '3.4.3.R34x_v20081215-1030'
 }
 
diff --git a/subprojects/docs/src/samples/scala/customizedLayout/build.gradle b/subprojects/docs/src/samples/scala/customizedLayout/build.gradle
index 1126ddd..f62c322 100644
--- a/subprojects/docs/src/samples/scala/customizedLayout/build.gradle
+++ b/subprojects/docs/src/samples/scala/customizedLayout/build.gradle
@@ -9,7 +9,7 @@ dependencies {
     scalaTools 'org.scala-lang:scala-library:2.8.1'
 
     compile 'org.scala-lang:scala-library:2.8.1'
-    testCompile group: 'junit', name: 'junit', version: '4.7'
+    testCompile group: 'junit', name: 'junit', version: '4.8.2'
 }
 
 // START SNIPPET define-main
diff --git a/subprojects/docs/src/samples/scala/fsc/build.gradle b/subprojects/docs/src/samples/scala/fsc/build.gradle
new file mode 100644
index 0000000..2545187
--- /dev/null
+++ b/subprojects/docs/src/samples/scala/fsc/build.gradle
@@ -0,0 +1,33 @@
+apply plugin: 'scala'
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    // Libraries needed to run the scala tools
+    scalaTools 'org.scala-lang:scala-compiler:2.8.1'
+    scalaTools 'org.scala-lang:scala-library:2.8.1'
+
+    // Libraries needed for scala api
+    compile 'org.scala-lang:scala-library:2.8.1'
+}
+
+dependencies {
+    compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
+    testCompile group: 'junit', name: 'junit', version: '4.8.2'
+}
+
+// START SNIPPET use-fsc
+compileScala {
+    scalaCompileOptions.useCompileDaemon = true
+
+    // optionally specify host and port of the daemon:
+    scalaCompileOptions.daemonServer = "localhost:4243"
+}
+// END SNIPPET use-fsc
+
+compileScala {
+    // don't use daemon because it would cause problems for automated testing
+    scalaCompileOptions.useCompileDaemon = false
+}
diff --git a/subprojects/docs/src/samples/scala/fsc/readme.xml b/subprojects/docs/src/samples/scala/fsc/readme.xml
new file mode 100644
index 0000000..509c320
--- /dev/null
+++ b/subprojects/docs/src/samples/scala/fsc/readme.xml
@@ -0,0 +1,3 @@
+<sample>
+    <para>Sala project using the Fast Scala Compiler (fsc).</para>
+</sample>
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/scala/fsc/src/main/scala/org/gradle/sample/api/Person.scala b/subprojects/docs/src/samples/scala/fsc/src/main/scala/org/gradle/sample/api/Person.scala
new file mode 100644
index 0000000..5effb67
--- /dev/null
+++ b/subprojects/docs/src/samples/scala/fsc/src/main/scala/org/gradle/sample/api/Person.scala
@@ -0,0 +1,9 @@
+package org.gradle.sample.api
+
+/**
+ * Defines the interface for a person.
+ */
+abstract trait Person
+{
+  def names: List[String]
+}
diff --git a/subprojects/docs/src/samples/scala/fsc/src/main/scala/org/gradle/sample/impl/PersonImpl.scala b/subprojects/docs/src/samples/scala/fsc/src/main/scala/org/gradle/sample/impl/PersonImpl.scala
new file mode 100644
index 0000000..c6743e9
--- /dev/null
+++ b/subprojects/docs/src/samples/scala/fsc/src/main/scala/org/gradle/sample/impl/PersonImpl.scala
@@ -0,0 +1,12 @@
+package org.gradle.sample.impl
+
+import org.gradle.sample.api.Person
+import org.apache.commons.collections.list.GrowthList;
+
+/**
+ * Immutable implementation of {@link Person}.
+ */
+class PersonImpl(val names: List[String]) extends Person
+{
+  private val importedList = new GrowthList();
+}
diff --git a/subprojects/docs/src/samples/scala/mixedJavaAndScala/build.gradle b/subprojects/docs/src/samples/scala/mixedJavaAndScala/build.gradle
index 7de1015..da2a4c3 100644
--- a/subprojects/docs/src/samples/scala/mixedJavaAndScala/build.gradle
+++ b/subprojects/docs/src/samples/scala/mixedJavaAndScala/build.gradle
@@ -11,5 +11,5 @@ dependencies {
     scalaTools 'org.scala-lang:scala-library:2.8.1'
 
     compile 'org.scala-lang:scala-library:2.8.1'
-    testCompile group: 'junit', name: 'junit', version: '4.7'
+    testCompile group: 'junit', name: 'junit', version: '4.8.2'
 }
diff --git a/subprojects/docs/src/samples/scala/quickstart/build.gradle b/subprojects/docs/src/samples/scala/quickstart/build.gradle
index 4822853..6f82628 100644
--- a/subprojects/docs/src/samples/scala/quickstart/build.gradle
+++ b/subprojects/docs/src/samples/scala/quickstart/build.gradle
@@ -20,5 +20,5 @@ dependencies {
 
 dependencies {
     compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
-    testCompile group: 'junit', name: 'junit', version: '4.7'
+    testCompile group: 'junit', name: 'junit', version: '4.8.2'
 }
diff --git a/subprojects/docs/src/samples/sonar/build.gradle b/subprojects/docs/src/samples/sonar/build.gradle
new file mode 100644
index 0000000..ab14832
--- /dev/null
+++ b/subprojects/docs/src/samples/sonar/build.gradle
@@ -0,0 +1,25 @@
+apply plugin: "java"
+
+// START SNIPPET use-plugin
+apply plugin: "sonar"
+// END SNIPPET use-plugin
+
+// START SNIPPET connection-settings
+sonar {
+    serverUrl = "http://my.server.com"
+
+    globalProperty "sonar.jdbc.url", "jdbc:mysql://my.server.com/sonar"
+    globalProperty "sonar.jdbc.driverClassName", "com.mysql.jdbc.Driver"
+    globalProperty "sonar.jdbc.username", "myusername"
+    globalProperty "sonar.jdbc.password", "mypassword"
+}
+// END SNIPPET connection-settings
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
+    testCompile group: 'junit', name: 'junit', version: '4.+'
+}
diff --git a/subprojects/docs/src/samples/sonar/src/main/java/org/gradle/Person.java b/subprojects/docs/src/samples/sonar/src/main/java/org/gradle/Person.java
new file mode 100644
index 0000000..8b69988
--- /dev/null
+++ b/subprojects/docs/src/samples/sonar/src/main/java/org/gradle/Person.java
@@ -0,0 +1,16 @@
+package org.gradle;
+
+import org.apache.commons.collections.list.GrowthList;
+
+public class Person {
+    private final String name;
+
+    public Person(String name) {
+        this.name = name;
+        new GrowthList();
+    }
+
+    public String getName() {
+        return name;
+    }
+}
diff --git a/subprojects/docs/src/samples/sonar/src/test/java/org/gradle/PersonTest.java b/subprojects/docs/src/samples/sonar/src/test/java/org/gradle/PersonTest.java
new file mode 100644
index 0000000..29fb813
--- /dev/null
+++ b/subprojects/docs/src/samples/sonar/src/test/java/org/gradle/PersonTest.java
@@ -0,0 +1,12 @@
+package org.gradle;
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class PersonTest {
+    @Test
+    public void canConstructAPersonWithAName() {
+        Person person = new Person("Larry");
+        assertEquals("Larry", person.getName());
+    }
+}
diff --git a/subprojects/docs/src/samples/testng/java-jdk15-passing/build.gradle b/subprojects/docs/src/samples/testng/java-jdk15-passing/build.gradle
index 75ef980..e589ea9 100644
--- a/subprojects/docs/src/samples/testng/java-jdk15-passing/build.gradle
+++ b/subprojects/docs/src/samples/testng/java-jdk15-passing/build.gradle
@@ -7,7 +7,7 @@ repositories {
 }
 
 dependencies {
-    testCompile 'org.testng:testng:5.13.1'
+    testCompile 'org.testng:testng:5.14.10'
 }
 
 test {
diff --git a/subprojects/docs/src/samples/testng/suitexmlbuilder/build.gradle b/subprojects/docs/src/samples/testng/suitexmlbuilder/build.gradle
index 28e8dbd..0f8e7b4 100644
--- a/subprojects/docs/src/samples/testng/suitexmlbuilder/build.gradle
+++ b/subprojects/docs/src/samples/testng/suitexmlbuilder/build.gradle
@@ -5,7 +5,7 @@ repositories {
 }
 
 dependencies {
-    testCompile 'org.testng:testng:5.13.1'
+    testCompile 'org.testng:testng:5.14.10'
 }
 
 test {
diff --git a/subprojects/docs/src/samples/toolingApi/build.gradle b/subprojects/docs/src/samples/toolingApi/build.gradle
deleted file mode 100644
index c184937..0000000
--- a/subprojects/docs/src/samples/toolingApi/build.gradle
+++ /dev/null
@@ -1,26 +0,0 @@
-apply plugin: 'java'
-
-if (!hasProperty('toolingApiVersion')) {
-    toolingApiVersion = gradle.gradleVersion
-}
-if (!hasProperty('toolingApiRepo')) {
-    toolingApiRepo = 'http://gradle.artifactoryonline.com/gradle/libs-releases-local'
-}
-
-repositories {
-    mavenRepo urls: toolingApiRepo
-    mavenCentral()
-}
-
-dependencies {
-    compile "org.gradle:gradle-tooling-api:${toolingApiVersion}"
-}
-
-task run(type: JavaExec) {
-    dependsOn sourceSets.main.runtimeClasspath.buildDependencies
-    main = 'org.gradle.sample.Main'
-    classpath = sourceSets.main.runtimeClasspath
-    if (project.hasProperty('gradleDistribution')) {
-        args = [gradleDistribution]
-    }
-}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/toolingApi/build/build.gradle b/subprojects/docs/src/samples/toolingApi/build/build.gradle
new file mode 100644
index 0000000..51571ad
--- /dev/null
+++ b/subprojects/docs/src/samples/toolingApi/build/build.gradle
@@ -0,0 +1,28 @@
+apply plugin: 'java'
+apply plugin: 'application'
+
+if (!hasProperty('toolingApiVersion')) {
+    toolingApiVersion = gradle.gradleVersion
+}
+if (!hasProperty('toolingApiRepo')) {
+    toolingApiRepo = 'http://repo.gradle.org/gradle/libs-releases-local'
+}
+
+repositories {
+    mavenRepo urls: toolingApiRepo
+    mavenCentral()
+}
+
+dependencies {
+    compile "org.gradle:gradle-tooling-api:${toolingApiVersion}"
+    // Need an SLF4J implementation at runtime
+    runtime 'org.slf4j:slf4j-simple:1.6.1'
+}
+
+mainClassName = 'org.gradle.sample.Main'
+
+run {
+    if (project.hasProperty('gradleDistribution')) {
+        args = [gradleDistribution]
+    }
+}
diff --git a/subprojects/docs/src/samples/toolingApi/build/readme.xml b/subprojects/docs/src/samples/toolingApi/build/readme.xml
new file mode 100644
index 0000000..6a5484f
--- /dev/null
+++ b/subprojects/docs/src/samples/toolingApi/build/readme.xml
@@ -0,0 +1,3 @@
+<sample>
+    <para>An application which uses the tooling API to execute a Gradle build.</para>
+</sample>
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/toolingApi/build/src/main/java/org/gradle/sample/Main.java b/subprojects/docs/src/samples/toolingApi/build/src/main/java/org/gradle/sample/Main.java
new file mode 100644
index 0000000..d58e265
--- /dev/null
+++ b/subprojects/docs/src/samples/toolingApi/build/src/main/java/org/gradle/sample/Main.java
@@ -0,0 +1,35 @@
+package org.gradle.sample;
+
+import org.gradle.tooling.BuildLauncher;
+import org.gradle.tooling.GradleConnector;
+import org.gradle.tooling.ProjectConnection;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+
+public class Main {
+    public static void main(String[] args) {
+        // Configure the connector and create the connection
+        GradleConnector connector = GradleConnector.newConnector();
+        connector.forProjectDirectory(new File("."));
+        if (args.length > 0) {
+            connector.useInstallation(new File(args[0]));
+        }
+
+        ProjectConnection connection = connector.connect();
+        try {
+            // Configure the build
+            BuildLauncher launcher = connection.newBuild();
+            launcher.forTasks("help");
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            launcher.setStandardOutput(outputStream);
+            launcher.setStandardError(outputStream);
+
+            // Run the build
+            launcher.run();
+        } finally {
+            // Clean up
+            connection.close();
+        }
+    }
+}
diff --git a/subprojects/docs/src/samples/toolingApi/model/build.gradle b/subprojects/docs/src/samples/toolingApi/model/build.gradle
new file mode 100644
index 0000000..51571ad
--- /dev/null
+++ b/subprojects/docs/src/samples/toolingApi/model/build.gradle
@@ -0,0 +1,28 @@
+apply plugin: 'java'
+apply plugin: 'application'
+
+if (!hasProperty('toolingApiVersion')) {
+    toolingApiVersion = gradle.gradleVersion
+}
+if (!hasProperty('toolingApiRepo')) {
+    toolingApiRepo = 'http://repo.gradle.org/gradle/libs-releases-local'
+}
+
+repositories {
+    mavenRepo urls: toolingApiRepo
+    mavenCentral()
+}
+
+dependencies {
+    compile "org.gradle:gradle-tooling-api:${toolingApiVersion}"
+    // Need an SLF4J implementation at runtime
+    runtime 'org.slf4j:slf4j-simple:1.6.1'
+}
+
+mainClassName = 'org.gradle.sample.Main'
+
+run {
+    if (project.hasProperty('gradleDistribution')) {
+        args = [gradleDistribution]
+    }
+}
diff --git a/subprojects/docs/src/samples/toolingApi/model/readme.xml b/subprojects/docs/src/samples/toolingApi/model/readme.xml
new file mode 100644
index 0000000..e297f11
--- /dev/null
+++ b/subprojects/docs/src/samples/toolingApi/model/readme.xml
@@ -0,0 +1,3 @@
+<sample>
+    <para>An application which uses the tooling API to build the model for a project.</para>
+</sample>
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/toolingApi/model/src/main/java/org/gradle/sample/Main.java b/subprojects/docs/src/samples/toolingApi/model/src/main/java/org/gradle/sample/Main.java
new file mode 100644
index 0000000..07bb189
--- /dev/null
+++ b/subprojects/docs/src/samples/toolingApi/model/src/main/java/org/gradle/sample/Main.java
@@ -0,0 +1,39 @@
+package org.gradle.sample;
+
+import org.gradle.tooling.GradleConnector;
+import org.gradle.tooling.ProjectConnection;
+import org.gradle.tooling.model.ExternalDependency;
+import org.gradle.tooling.model.eclipse.EclipseProject;
+import org.gradle.tooling.model.eclipse.EclipseSourceDirectory;
+
+import java.io.File;
+
+public class Main {
+    public static void main(String[] args) {
+        // Configure the connector and create the connection
+        GradleConnector connector = GradleConnector.newConnector();
+        connector.forProjectDirectory(new File("."));
+        if (args.length > 0) {
+            connector.useInstallation(new File(args[0]));
+        }
+
+        ProjectConnection connection = connector.connect();
+        try {
+            // Load the Eclipse model for the project
+            EclipseProject project = connection.getModel(EclipseProject.class);
+            System.out.println("Project: " + project.getName());
+            System.out.println("Project directory: " + project.getProjectDirectory());
+            System.out.println("Source directories:");
+            for (EclipseSourceDirectory srcDir : project.getSourceDirectories()) {
+                System.out.println(srcDir.getPath());
+            }
+            System.out.println("Project classpath:");
+            for (ExternalDependency externalDependency : project.getClasspath()) {
+                System.out.println(externalDependency.getFile().getName());
+            }
+        } finally {
+            // Clean up
+            connection.close();
+        }
+    }
+}
diff --git a/subprojects/docs/src/samples/toolingApi/src/main/java/org/gradle/sample/Main.java b/subprojects/docs/src/samples/toolingApi/src/main/java/org/gradle/sample/Main.java
deleted file mode 100644
index d6eafd6..0000000
--- a/subprojects/docs/src/samples/toolingApi/src/main/java/org/gradle/sample/Main.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.gradle.sample;
-
-import org.gradle.tooling.GradleConnector;
-import org.gradle.tooling.BuildConnection;
-import org.gradle.tooling.model.ExternalDependency;
-import org.gradle.tooling.model.eclipse.EclipseBuild;
-
-import java.io.File;
-
-public class Main {
-    public static void main(String[] args) {
-        GradleConnector connector = GradleConnector.newConnector();
-        try {
-            // Configure the connector and create the connection
-            connector.forProjectDirectory(new File("."));
-            if (args.length > 0) {
-                connector.useInstallation(new File(args[0]));
-            }
-            BuildConnection connection = connector.connect();
-
-            // Load the Eclipse model for the project
-            EclipseBuild build = connection.getModel(EclipseBuild.class);
-            System.out.println("Project classpath:");
-            for (ExternalDependency externalDependency : build.getRootProject().getClasspath()) {
-                System.out.println(externalDependency.getFile().getName());
-            }
-        } finally {
-            // Clean up
-            connector.close();
-        }
-    }
-}
diff --git a/subprojects/docs/src/samples/userguide/artifacts/defineRepository/build.gradle b/subprojects/docs/src/samples/userguide/artifacts/defineRepository/build.gradle
index 9fc01af..5734af1 100644
--- a/subprojects/docs/src/samples/userguide/artifacts/defineRepository/build.gradle
+++ b/subprojects/docs/src/samples/userguide/artifacts/defineRepository/build.gradle
@@ -44,6 +44,25 @@ repositories {
 //END SNIPPET flat-dir
 //END SNIPPET flat-dir-multi
 
+//START SNIPPET ivy-repo
+repositories {
+    ivy {
+        name = 'ivyRepo'
+        artifactPattern "http://repo.mycompany.com/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]"
+    }
+}
+//END SNIPPET ivy-repo
+
+//START SNIPPET authenticated-ivy-repo
+repositories {
+    ivy {
+        name = 'privateIvyRepo'
+        userName = 'user'
+        password = 'password'
+        artifactPattern "http://repo.mycompany.com/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]"
+    }
+}
+//END SNIPPET authenticated-ivy-repo
 
 task lookup << {
     //START SNIPPET lookup-resolver
diff --git a/subprojects/docs/src/samples/userguide/artifacts/externalDependencies/build.gradle b/subprojects/docs/src/samples/userguide/artifacts/externalDependencies/build.gradle
index 0dc3ad4..33d0103 100644
--- a/subprojects/docs/src/samples/userguide/artifacts/externalDependencies/build.gradle
+++ b/subprojects/docs/src/samples/userguide/artifacts/externalDependencies/build.gradle
@@ -42,7 +42,7 @@ dependencies {
 
 //START SNIPPET dependencies-with-empty-attributes
 dependencies {
-    runtime ":junit:4.4", ":testng"
+    runtime ":junit:4.8.2", ":testng"
     runtime name: 'testng' 
 }
 //END SNIPPET dependencies-with-empty-attributes
@@ -63,12 +63,12 @@ dependencies {
 
 //START SNIPPET client-modules
 dependencies {
-    runtime module("org.codehaus.groovy:groovy-all:1.7.6") {
+    runtime module("org.codehaus.groovy:groovy-all:1.7.10") {
         dependency("commons-cli:commons-cli:1.0") {
             transitive = false
         }
-        module(group: 'org.apache.ant', name: 'ant', version: '1.7.0') {
-            dependencies "org.apache.ant:ant-launcher:1.7.0 at jar", "org.apache.ant:ant-junit:1.7.0"
+        module(group: 'org.apache.ant', name: 'ant', version: '1.8.2') {
+            dependencies "org.apache.ant:ant-launcher:1.8.2 at jar", "org.apache.ant:ant-junit:1.8.2"
         }
     }
 }
@@ -82,9 +82,9 @@ dependencies {
 //END SNIPPET file-dependencies
 
 //START SNIPPET list-grouping
-List groovy = ["org.codehaus.groovy:groovy-all:1.7.6 at jar",
+List groovy = ["org.codehaus.groovy:groovy-all:1.7.10 at jar",
                "commons-cli:commons-cli:1.0 at jar",
-               "org.apache.ant:ant:1.7.0 at jar"]
+               "org.apache.ant:ant:1.8.2 at jar"]
 List hibernate = ['org.hibernate:hibernate:3.0.5 at jar', 'somegroup:someorg:1.0 at jar']
 dependencies {
 	runtime groovy, hibernate
diff --git a/subprojects/docs/src/samples/userguide/initScripts/customLogger/init.gradle b/subprojects/docs/src/samples/userguide/initScripts/customLogger/init.gradle
index bbf0bce..3e01609 100644
--- a/subprojects/docs/src/samples/userguide/initScripts/customLogger/init.gradle
+++ b/subprojects/docs/src/samples/userguide/initScripts/customLogger/init.gradle
@@ -1,6 +1,6 @@
 useLogger(new CustomEventLogger())
 
-class CustomEventLogger extends BuildAdapter implements BuildListener, TaskExecutionListener {
+class CustomEventLogger extends BuildAdapter implements TaskExecutionListener {
 
     public void beforeExecute(Task task) {
         println "[$task.name]"
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/main/resources/someprops.properties b/subprojects/docs/src/samples/userguide/multiproject/addKrill/water/bluewhale/.ignore-me
similarity index 100%
copy from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/main/resources/someprops.properties
copy to subprojects/docs/src/samples/userguide/multiproject/addKrill/water/bluewhale/.ignore-me
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/test/resources/someprops.properties b/subprojects/docs/src/samples/userguide/multiproject/addKrill/water/krill/.ignore-me
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/test/resources/someprops.properties
rename to subprojects/docs/src/samples/userguide/multiproject/addKrill/water/krill/.ignore-me
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/src/main/groovy/script.groovy b/subprojects/docs/src/samples/userguide/multiproject/addSpecifics/water/bluewhale/.ignore-me
similarity index 100%
copy from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/src/main/groovy/script.groovy
copy to subprojects/docs/src/samples/userguide/multiproject/addSpecifics/water/bluewhale/.ignore-me
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/src/main/resources/someprops.properties b/subprojects/docs/src/samples/userguide/multiproject/addSpecifics/water/krill/.ignore-me
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/src/main/resources/someprops.properties
rename to subprojects/docs/src/samples/userguide/multiproject/addSpecifics/water/krill/.ignore-me
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/src/test/resources/someprops.properties b/subprojects/docs/src/samples/userguide/multiproject/addTropical/water/tropicalFish/.ignore-me
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/src/test/resources/someprops.properties
rename to subprojects/docs/src/samples/userguide/multiproject/addTropical/water/tropicalFish/.ignore-me
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/sub/ignore/bad.file b/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/api/src/main/resources/org/gradle/resource.txt
similarity index 100%
copy from subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/sub/ignore/bad.file
copy to subprojects/docs/src/samples/userguide/multiproject/dependencies/java/api/src/main/resources/org/gradle/resource.txt
diff --git a/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/api/src/test/java/org/gradle/PersonTest.java b/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/api/src/test/java/org/gradle/PersonTest.java
new file mode 100644
index 0000000..5c0d2ea
--- /dev/null
+++ b/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/api/src/test/java/org/gradle/PersonTest.java
@@ -0,0 +1,9 @@
+package org.gradle;
+
+import org.junit.Test;
+
+public class PersonTest {
+    @Test
+    public void ok() {
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/sub/onesub.a b/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/api/src/test/resources/org/gradle/test-resource.txt
similarity index 100%
copy from subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/sub/onesub.a
copy to subprojects/docs/src/samples/userguide/multiproject/dependencies/java/api/src/test/resources/org/gradle/test-resource.txt
diff --git a/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/build.gradle b/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/build.gradle
index 9783816..834ee63 100644
--- a/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/build.gradle
+++ b/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/build.gradle
@@ -1,10 +1,13 @@
 subprojects {
-    repositories {
-        mavenCentral()
-    }
     apply plugin: 'java'
     group = 'org.gradle.sample'
     version = '1.0'
+    repositories {
+        mavenCentral()
+    }
+    dependencies {
+        testCompile "junit:junit:4.8.2"
+    }
 }
 
 project(':api') {
@@ -16,7 +19,6 @@ project(':api') {
 project(':services:personService') {
     dependencies {
         compile project(':shared'), project(':api')
-        testCompile "junit:junit:3.8.2"
     }
 }
 
diff --git a/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/services/personService/src/main/java/org/gradle/sample/services/PersonService.java b/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/services/personService/src/main/java/org/gradle/sample/services/PersonService.java
index 9c17e22..40c65e4 100644
--- a/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/services/personService/src/main/java/org/gradle/sample/services/PersonService.java
+++ b/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/services/personService/src/main/java/org/gradle/sample/services/PersonService.java
@@ -1,4 +1,4 @@
-package org.gradle.api.services;
+package org.gradle.sample.services;
 
 import org.gradle.sample.api.Person;
 import org.gradle.sample.shared.Helper;
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/sub/onesub.b b/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/services/personService/src/main/resources/org/gradle/resource.txt
similarity index 100%
copy from subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/sub/onesub.b
copy to subprojects/docs/src/samples/userguide/multiproject/dependencies/java/services/personService/src/main/resources/org/gradle/resource.txt
diff --git a/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/services/personService/src/test/java/org/gradle/sample/services/PersonServiceTest.java b/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/services/personService/src/test/java/org/gradle/sample/services/PersonServiceTest.java
index 73e9b59..c31e998 100644
--- a/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/services/personService/src/test/java/org/gradle/sample/services/PersonServiceTest.java
+++ b/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/services/personService/src/test/java/org/gradle/sample/services/PersonServiceTest.java
@@ -1,4 +1,4 @@
-package org.gradle.api.services;
+package org.gradle.sample.services;
 
 import junit.framework.TestCase;
 import org.gradle.sample.apiImpl.PersonImpl;
diff --git a/subprojects/docs/src/samples/userguide/multiproject/addKrill/water/bluewhale/.gitignore b/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/services/personService/src/test/resources/org/gradle/test-resource.txt
similarity index 100%
rename from subprojects/docs/src/samples/userguide/multiproject/addKrill/water/bluewhale/.gitignore
rename to subprojects/docs/src/samples/userguide/multiproject/dependencies/java/services/personService/src/test/resources/org/gradle/test-resource.txt
diff --git a/subprojects/docs/src/samples/userguide/multiproject/addKrill/water/krill/.gitignore b/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/shared/src/main/resources/org/gradle/resource.txt
similarity index 100%
rename from subprojects/docs/src/samples/userguide/multiproject/addKrill/water/krill/.gitignore
rename to subprojects/docs/src/samples/userguide/multiproject/dependencies/java/shared/src/main/resources/org/gradle/resource.txt
diff --git a/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/shared/src/test/java/org/gradle/HelperTest.java b/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/shared/src/test/java/org/gradle/HelperTest.java
new file mode 100644
index 0000000..942e0f8
--- /dev/null
+++ b/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/shared/src/test/java/org/gradle/HelperTest.java
@@ -0,0 +1,9 @@
+package org.gradle;
+
+import org.junit.Test;
+
+public class HelperTest {
+    @Test
+    public void ok() {
+    }
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/multiproject/addSpecifics/water/bluewhale/.gitignore b/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/shared/src/test/resources/org/gradle/test-resource.txt
similarity index 100%
rename from subprojects/docs/src/samples/userguide/multiproject/addSpecifics/water/bluewhale/.gitignore
rename to subprojects/docs/src/samples/userguide/multiproject/dependencies/java/shared/src/test/resources/org/gradle/test-resource.txt
diff --git a/subprojects/docs/src/samples/userguide/multiproject/dependencies/javaWithCustomConf/build.gradle b/subprojects/docs/src/samples/userguide/multiproject/dependencies/javaWithCustomConf/build.gradle
index 78b4076..5be51d7 100644
--- a/subprojects/docs/src/samples/userguide/multiproject/dependencies/javaWithCustomConf/build.gradle
+++ b/subprojects/docs/src/samples/userguide/multiproject/dependencies/javaWithCustomConf/build.gradle
@@ -26,6 +26,6 @@ project(':services:personService') {
     dependencies {
         compile project(':shared')
         compile project(path: ':api', configuration: 'spi')
-        testCompile "junit:junit:3.8.2", project(':api')
+        testCompile "junit:junit:4.8.2", project(':api')
     }
 }
diff --git a/subprojects/docs/src/samples/userguide/multiproject/addSpecifics/water/krill/.gitignore b/subprojects/docs/src/samples/userguide/multiproject/firstExample/water/bluewhale/.ignore-me
similarity index 100%
rename from subprojects/docs/src/samples/userguide/multiproject/addSpecifics/water/krill/.gitignore
rename to subprojects/docs/src/samples/userguide/multiproject/firstExample/water/bluewhale/.ignore-me
diff --git a/subprojects/docs/src/samples/userguide/multiproject/addTropical/water/tropicalFish/.gitignore b/subprojects/docs/src/samples/userguide/multiproject/flat/dolphin/.ignore-me
similarity index 100%
rename from subprojects/docs/src/samples/userguide/multiproject/addTropical/water/tropicalFish/.gitignore
rename to subprojects/docs/src/samples/userguide/multiproject/flat/dolphin/.ignore-me
diff --git a/subprojects/docs/src/samples/userguide/multiproject/firstExample/water/bluewhale/.gitignore b/subprojects/docs/src/samples/userguide/multiproject/flat/shark/.ignore-me
similarity index 100%
rename from subprojects/docs/src/samples/userguide/multiproject/firstExample/water/bluewhale/.gitignore
rename to subprojects/docs/src/samples/userguide/multiproject/flat/shark/.ignore-me
diff --git a/subprojects/docs/src/samples/userguide/multiproject/flat/dolphin/.gitignore b/subprojects/docs/src/samples/userguide/multiproject/flatWithNoDefaultMaster/dolphin/.ignore-me
similarity index 100%
rename from subprojects/docs/src/samples/userguide/multiproject/flat/dolphin/.gitignore
rename to subprojects/docs/src/samples/userguide/multiproject/flatWithNoDefaultMaster/dolphin/.ignore-me
diff --git a/subprojects/docs/src/samples/userguide/multiproject/flat/shark/.gitignore b/subprojects/docs/src/samples/userguide/multiproject/flatWithNoDefaultMaster/shark/.ignore-me
similarity index 100%
rename from subprojects/docs/src/samples/userguide/multiproject/flat/shark/.gitignore
rename to subprojects/docs/src/samples/userguide/multiproject/flatWithNoDefaultMaster/shark/.ignore-me
diff --git a/subprojects/docs/src/samples/userguide/multiproject/flatWithNoDefaultMaster/dolphin/.gitignore b/subprojects/docs/src/samples/userguide/multiproject/subprojectsAddFromTop/water/bluewhale/.ignore-me
similarity index 100%
rename from subprojects/docs/src/samples/userguide/multiproject/flatWithNoDefaultMaster/dolphin/.gitignore
rename to subprojects/docs/src/samples/userguide/multiproject/subprojectsAddFromTop/water/bluewhale/.ignore-me
diff --git a/subprojects/docs/src/samples/userguide/multiproject/flatWithNoDefaultMaster/shark/.gitignore b/subprojects/docs/src/samples/userguide/multiproject/subprojectsAddFromTop/water/krill/.ignore-me
similarity index 100%
rename from subprojects/docs/src/samples/userguide/multiproject/flatWithNoDefaultMaster/shark/.gitignore
rename to subprojects/docs/src/samples/userguide/multiproject/subprojectsAddFromTop/water/krill/.ignore-me
diff --git a/subprojects/docs/src/samples/userguide/multiproject/subprojectsAddFromTop/water/bluewhale/.gitignore b/subprojects/docs/src/samples/userguide/multiproject/useSubprojects/water/bluewhale/.ignore-me
similarity index 100%
rename from subprojects/docs/src/samples/userguide/multiproject/subprojectsAddFromTop/water/bluewhale/.gitignore
rename to subprojects/docs/src/samples/userguide/multiproject/useSubprojects/water/bluewhale/.ignore-me
diff --git a/subprojects/docs/src/samples/userguide/multiproject/useSubprojects/water/krill/.gitignore b/subprojects/docs/src/samples/userguide/multiproject/useSubprojects/water/krill/.gitignore
deleted file mode 100644
index e69de29..0000000
diff --git a/subprojects/docs/src/samples/userguide/multiproject/subprojectsAddFromTop/water/krill/.gitignore b/subprojects/docs/src/samples/userguide/multiproject/useSubprojects/water/krill/.ignore-me
similarity index 100%
rename from subprojects/docs/src/samples/userguide/multiproject/subprojectsAddFromTop/water/krill/.gitignore
rename to subprojects/docs/src/samples/userguide/multiproject/useSubprojects/water/krill/.ignore-me
diff --git a/subprojects/docs/src/samples/userguide/organizeBuildLogic/build.gradle b/subprojects/docs/src/samples/userguide/organizeBuildLogic/build.gradle
index 275dbaa..4528f60 100644
--- a/subprojects/docs/src/samples/userguide/organizeBuildLogic/build.gradle
+++ b/subprojects/docs/src/samples/userguide/organizeBuildLogic/build.gradle
@@ -3,7 +3,7 @@ configurations {
 }
 
 dependencies {
-    ftpAntTask("org.apache.ant:ant-commons-net:1.7.0") {
+    ftpAntTask("org.apache.ant:ant-commons-net:1.8.2") {
         module("commons-net:commons-net:1.4.1") {
             dependencies "oro:oro:2.0.8:jar"
         }
diff --git a/subprojects/docs/src/samples/userguide/tutorial/antChecksum/build.gradle b/subprojects/docs/src/samples/userguide/tutorial/antChecksum/build.gradle
deleted file mode 100644
index 0be3cef..0000000
--- a/subprojects/docs/src/samples/userguide/tutorial/antChecksum/build.gradle
+++ /dev/null
@@ -1,10 +0,0 @@
-task checksum << {
-    def files = file('../antChecksumFiles').listFiles().sort()
-    files.each { File file ->
-        if (file.isFile()) {
-            ant.checksum(file: file, property: file.name)
-            println "$file.name Checksum: ${ant.properties[file.name]}"
-        }
-    }
-}
-        
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/tutorial/antChecksumFiles/agile_manifesto.html b/subprojects/docs/src/samples/userguide/tutorial/antChecksumFiles/agile_manifesto.html
deleted file mode 100644
index 59f8430..0000000
--- a/subprojects/docs/src/samples/userguide/tutorial/antChecksumFiles/agile_manifesto.html
+++ /dev/null
@@ -1,78 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
-<html>
-<head>
-<title>Manifesto for Agile Software Development</title>
-<META name="description" content="We are uncovering better ways of developing software by doing it and helping others do it. These are our values and principles.">
-<META name="keywords" content="agilemanifesto agilealliance alliance manifesto agile programming, agile development, extreme programming, XP, software project management, iterative development, collaborative development, software engineering best practices, software development best practices">
-</head>
-<body background=background.jpg>
-<center>
-<br><br><br><br>
-
-<h1>Manifesto for Agile Software Development</h1>
-<br><br><br>
-
-<p>
-
-<font size="+2">
-We are uncovering better ways of developing <br>
-software by doing it and helping others do it. <br>
-Through this work we have come to value:
-</font>
-
-<p>
-<font size="+3">Individuals and interactions </font><font size="+2">over processes and tools</font> <br>
-<font size="+3">Working software </font><font size="+2">over comprehensive documentation</font> <br>
-
-<font size="+3">Customer collaboration </font><font size="+2">over contract negotiation</font> <br>
-<font size="+3">Responding to change </font><font size="+2">over following a plan</font> <br>
-
-<p>
-<font size="+2">
-That is, while there is value in the items on <br>
-the right, we value the items on the left more.
-</font>
-<br><br><br><br>
-
-<table cellpadding=15><tr align=center valign=top>
-<td><font size="+2">
-Kent Beck<br>
-Mike Beedle<br>
-Arie van Bennekum<br>
-Alistair Cockburn<br>
-Ward Cunningham<br>
-Martin Fowler<br>
-</font></td>
-
-<td><font size="+2">
-
-James Grenning<br>
-Jim Highsmith<br>
-Andrew Hunt<br>
-Ron Jeffries<br>
-Jon Kern<br>
-Brian Marick<br>
-</font></td>
-
-<td><font size="+2">
-Robert C. Martin<br>
-
-Steve Mellor<br>
-Ken Schwaber<br>
-Jeff Sutherland<br>
-Dave Thomas<br>
-</font></td>
-</tr></table>
-<br><br><br>
-
-<font size=1 color=gray>
-© 2001, the above authors<br>
-this declaration may be freely copied in any form, <br>
-
-but only in its entirety through this notice.
-</font>
-<br><br>
-
-</center>
-</body>
-</html>
diff --git a/subprojects/docs/src/samples/userguide/tutorial/antChecksumFiles/agile_principles.html b/subprojects/docs/src/samples/userguide/tutorial/antChecksumFiles/agile_principles.html
deleted file mode 100644
index 0aa281d..0000000
--- a/subprojects/docs/src/samples/userguide/tutorial/antChecksumFiles/agile_principles.html
+++ /dev/null
@@ -1,89 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
-<HTML>
-<HEAD>
-<title>Principles behind the Agile Manifesto</title>
-<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
-</HEAD>
-<BODY>
-
-<br><br><br><br>
-<center><h1>Principles behind the Agile Manifesto</h1></center>
-
-<br><br>
-<center>
-<font size="+2">
-<i>We follow these principles:</i>
-
-</font>
-
-<p><font size="+2">
-Our highest priority is to satisfy the customer<br>
-through early and continuous delivery<br>
-of valuable software.
-</font></p>
-
-<p><font size="+2">
-Welcome changing requirements, even late in <br>
-development. Agile processes harness change for <br>
-the customer's competitive advantage.
-</font></p>
-
-<p><font size="+2">
-Deliver working software frequently, from a <br>
-couple of weeks to a couple of months, with a <br>
-preference to the shorter timescale.
-</font></p>
-
-<p><font size="+2">
-Business people and developers must work <br>
-together daily throughout the project.
-</font></p>
-
-<p><font size="+2">
-Build projects around motivated individuals. <br>
-
-Give them the environment and support they need, <br>
-and trust them to get the job done.
-</font></p>
-
-<p><font size="+2">
-The most efficient and effective method of <br>
-conveying information to and within a development <br>
-team is face-to-face conversation.
-</font></p>
-
-<p><font size="+2">
-Working software is the primary measure of progress.
-</font></p>
-
-<p><font size="+2">
-Agile processes promote sustainable development. <br>
-The sponsors, developers, and users should be able <br>
-to maintain a constant pace indefinitely.
-</font></p>
-
-<p><font size="+2">
-Continuous attention to technical excellence <br>
-and good design enhances agility.
-</font></p>
-
-<p><font size="+2">
-Simplicity--the art of maximizing the amount <br>
-
-of work not done--is essential.
-</font></p>
-
-<p><font size="+2">
-The best architectures, requirements, and designs <br>
-emerge from self-organizing teams.
-</font></p>
-
-<p><font size="+2">
-At regular intervals, the team reflects on how <br>
-to become more effective, then tunes and adjusts <br>
-its behavior accordingly.
-</font></p>
-<br><br><br>
-</center>
-</BODY>
-</HTML>
diff --git a/subprojects/docs/src/samples/userguide/tutorial/antChecksumFiles/dylan_thomas.txt b/subprojects/docs/src/samples/userguide/tutorial/antChecksumFiles/dylan_thomas.txt
deleted file mode 100644
index c8210fd..0000000
--- a/subprojects/docs/src/samples/userguide/tutorial/antChecksumFiles/dylan_thomas.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-And death shall have no dominion
-Dead men naked they shall be one
-With the man in the wind and the west moon
-When their bones are picked clean and the clean bones gone
-They shall have stars at elbow and foot
-Though they go mad they shall be sane
-Though they sink through the sea they shall rise again
-Though lovers be lost love shall not
-And death shall have no dominion.
diff --git a/subprojects/docs/src/samples/userguide/tutorial/antChecksumWithMethod/build.gradle b/subprojects/docs/src/samples/userguide/tutorial/antChecksumWithMethod/build.gradle
deleted file mode 100644
index e435c4b..0000000
--- a/subprojects/docs/src/samples/userguide/tutorial/antChecksumWithMethod/build.gradle
+++ /dev/null
@@ -1,17 +0,0 @@
-task checksum << {
-    fileList('../antChecksumFiles').each {File file ->
-        ant.checksum(file: file, property: "cs_$file.name")
-        println "$file.name Checksum: ${ant.properties["cs_$file.name"]}"
-    }
-}
-
-task length << {
-    fileList('../antChecksumFiles').each {File file ->
-        ant.length(file: file, property: "lt_$file.name")
-        println "$file.name Length: ${ant.properties["lt_$file.name"]}"
-    }
-}
-
-File[] fileList(String dir) {
-    file(dir).listFiles({file -> file.isFile() } as FileFilter).sort()
-}
diff --git a/subprojects/docs/src/samples/userguide/tutorial/antLoadfile/build.gradle b/subprojects/docs/src/samples/userguide/tutorial/antLoadfile/build.gradle
new file mode 100644
index 0000000..1fb1f15
--- /dev/null
+++ b/subprojects/docs/src/samples/userguide/tutorial/antLoadfile/build.gradle
@@ -0,0 +1,10 @@
+task loadfile << {
+    def files = file('../antLoadfileResources').listFiles().sort()
+    files.each { File file ->
+        if (file.isFile()) {
+            ant.loadfile(srcFile: file, property: file.name)
+            println " *** $file.name ***"
+            println "${ant.properties[file.name]}"
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/tutorial/antLoadfileResources/agile.manifesto.txt b/subprojects/docs/src/samples/userguide/tutorial/antLoadfileResources/agile.manifesto.txt
new file mode 100644
index 0000000..3d81398
--- /dev/null
+++ b/subprojects/docs/src/samples/userguide/tutorial/antLoadfileResources/agile.manifesto.txt
@@ -0,0 +1,4 @@
+Individuals and interactions over processes and tools
+Working software over comprehensive documentation
+Customer collaboration  over contract negotiation
+Responding to change over following a plan
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/tutorial/antLoadfileResources/gradle.manifesto.txt b/subprojects/docs/src/samples/userguide/tutorial/antLoadfileResources/gradle.manifesto.txt
new file mode 100644
index 0000000..e3aa066
--- /dev/null
+++ b/subprojects/docs/src/samples/userguide/tutorial/antLoadfileResources/gradle.manifesto.txt
@@ -0,0 +1,2 @@
+Make the impossible possible, make the possible easy and make the easy elegant.
+(inspired by Moshe Feldenkrais)
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/tutorial/antLoadfileWithMethod/build.gradle b/subprojects/docs/src/samples/userguide/tutorial/antLoadfileWithMethod/build.gradle
new file mode 100644
index 0000000..c905c09
--- /dev/null
+++ b/subprojects/docs/src/samples/userguide/tutorial/antLoadfileWithMethod/build.gradle
@@ -0,0 +1,17 @@
+task checksum << {
+    fileList('../antLoadfileResources').each {File file ->
+        ant.checksum(file: file, property: "cs_$file.name")
+        println "$file.name Checksum: ${ant.properties["cs_$file.name"]}"
+    }
+}
+
+task loadfile << {
+    fileList('../antLoadfileResources').each {File file ->
+        ant.loadfile(srcFile: file, property: file.name)
+        println "I'm fond of $file.name"
+    }
+}
+
+File[] fileList(String dir) {
+    file(dir).listFiles({file -> file.isFile() } as FileFilter).sort()
+}
diff --git a/subprojects/docs/src/samples/userguide/tutorial/groovy/build.gradle b/subprojects/docs/src/samples/userguide/tutorial/groovy/build.gradle
index 257ede7..ebca2d2 100644
--- a/subprojects/docs/src/samples/userguide/tutorial/groovy/build.gradle
+++ b/subprojects/docs/src/samples/userguide/tutorial/groovy/build.gradle
@@ -48,7 +48,7 @@ repositories({ println "in a closure" })
 // START SNIPPET closureDelegates
 dependencies {
     assert delegate == project.dependencies
-    compile('junit:junit:4.8.1')
-    delegate.compile('junit:junit:4.8.1')
+    compile('junit:junit:4.8.2')
+    delegate.compile('junit:junit:4.8.2')
 }
 // END SNIPPET closureDelegates
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/tutorial/projectReports/build.gradle b/subprojects/docs/src/samples/userguide/tutorial/projectReports/build.gradle
index 8df32e7..d7caa4a 100644
--- a/subprojects/docs/src/samples/userguide/tutorial/projectReports/build.gradle
+++ b/subprojects/docs/src/samples/userguide/tutorial/projectReports/build.gradle
@@ -63,7 +63,7 @@ project(':api') {
 description = 'The shared API for the application'
 // END SNIPPET project-description
     dependencies {
-        compile "org.codehaus.groovy:groovy-all:1.7.6"
+        compile "org.codehaus.groovy:groovy-all:1.7.10"
     }
 }
 
diff --git a/subprojects/docs/src/samples/userguideOutput/antChecksum.out b/subprojects/docs/src/samples/userguideOutput/antChecksum.out
deleted file mode 100644
index c496307..0000000
--- a/subprojects/docs/src/samples/userguideOutput/antChecksum.out
+++ /dev/null
@@ -1,3 +0,0 @@
-agile_manifesto.html Checksum: 2dd24e01676046d8dedc2009a1a8f563
-agile_principles.html Checksum: 659d204c8c7ccb5d633de0b0d26cd104
-dylan_thomas.txt Checksum: 91040ca1cefcbfdc8016b1b3e51f23d3
diff --git a/subprojects/docs/src/samples/userguideOutput/antChecksumWithMethod.out b/subprojects/docs/src/samples/userguideOutput/antChecksumWithMethod.out
deleted file mode 100644
index c496307..0000000
--- a/subprojects/docs/src/samples/userguideOutput/antChecksumWithMethod.out
+++ /dev/null
@@ -1,3 +0,0 @@
-agile_manifesto.html Checksum: 2dd24e01676046d8dedc2009a1a8f563
-agile_principles.html Checksum: 659d204c8c7ccb5d633de0b0d26cd104
-dylan_thomas.txt Checksum: 91040ca1cefcbfdc8016b1b3e51f23d3
diff --git a/subprojects/docs/src/samples/userguideOutput/antLoadfile.out b/subprojects/docs/src/samples/userguideOutput/antLoadfile.out
new file mode 100644
index 0000000..69249e8
--- /dev/null
+++ b/subprojects/docs/src/samples/userguideOutput/antLoadfile.out
@@ -0,0 +1,8 @@
+ *** agile.manifesto.txt ***
+Individuals and interactions over processes and tools
+Working software over comprehensive documentation
+Customer collaboration  over contract negotiation
+Responding to change over following a plan
+ *** gradle.manifesto.txt ***
+Make the impossible possible, make the possible easy and make the easy elegant.
+(inspired by Moshe Feldenkrais)
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguideOutput/antLoadfileWithMethod.out b/subprojects/docs/src/samples/userguideOutput/antLoadfileWithMethod.out
new file mode 100644
index 0000000..40031a2
--- /dev/null
+++ b/subprojects/docs/src/samples/userguideOutput/antLoadfileWithMethod.out
@@ -0,0 +1,2 @@
+I'm fond of agile.manifesto.txt
+I'm fond of gradle.manifesto.txt
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguideOutput/dependencyListReport.out b/subprojects/docs/src/samples/userguideOutput/dependencyListReport.out
index 1ccaad8..343e30b 100644
--- a/subprojects/docs/src/samples/userguideOutput/dependencyListReport.out
+++ b/subprojects/docs/src/samples/userguideOutput/dependencyListReport.out
@@ -10,7 +10,7 @@ Project :api - The shared API for the application
 ------------------------------------------------------------
 
 compile
-\--- org.codehaus.groovy:groovy-all:1.7.6 [default]
+\--- org.codehaus.groovy:groovy-all:1.7.10 [default]
 
 ------------------------------------------------------------
 Project :webapp - The Web application implementation
@@ -18,5 +18,5 @@ Project :webapp - The Web application implementation
 
 compile
 +--- projectReports:api:1.0-SNAPSHOT [compile]
-|    \--- org.codehaus.groovy:groovy-all:1.7.6 [default]
+|    \--- org.codehaus.groovy:groovy-all:1.7.10 [default]
 \--- commons-io:commons-io:1.2 [default]
diff --git a/subprojects/docs/src/samples/webApplication/customised/build.gradle b/subprojects/docs/src/samples/webApplication/customised/build.gradle
index b148244..9607ab1 100644
--- a/subprojects/docs/src/samples/webApplication/customised/build.gradle
+++ b/subprojects/docs/src/samples/webApplication/customised/build.gradle
@@ -26,7 +26,7 @@ dependencies {
     }
     runtime ":runtime:1.0"
     providedRuntime ":providedRuntime:1.0 at jar"
-    testCompile "junit:junit:3.8.2"
+    testCompile "junit:junit:4.8.2"
     moreLibs ":otherLib:1.0"
 }
 
diff --git a/subprojects/eclipse/eclipse.gradle b/subprojects/eclipse/eclipse.gradle
deleted file mode 100644
index 9fad845..0000000
--- a/subprojects/eclipse/eclipse.gradle
+++ /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.
- */
-dependencies {
-    groovy libraries.groovy_depends
-
-    compile project(':scala')
-    compile project(':core')
-    compile project(':plugins')
-    compile libraries.commons_io
-    compile libraries.slf4j_api, libraries.jaxen
-    compile 'dom4j:dom4j:1.6.1 at jar'
-
-
-    testCompile project(path: ':core', configuration: 'testFixtures')
-    testRuntime project(path: ':core', configuration: 'testFixturesRuntime')
-}
-
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipseClasspath.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipseClasspath.groovy
deleted file mode 100644
index 7f59d77..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipseClasspath.groovy
+++ /dev/null
@@ -1,104 +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.plugins.eclipse
-
-import org.gradle.api.artifacts.Configuration
-import org.gradle.api.tasks.SourceSet
-import org.gradle.api.tasks.XmlGeneratorTask
-import org.gradle.plugins.eclipse.model.Classpath
-import org.gradle.plugins.eclipse.model.Container
-import org.gradle.plugins.eclipse.model.internal.ClasspathFactory
-
-/**
- * Generates an Eclipse <code>.classpath</code> file.
- *
- * @author Hans Dockter
- */
-class EclipseClasspath extends XmlGeneratorTask<Classpath> {
-    /**
-     * The source sets to be added to the classpath.
-     */
-    Iterable<SourceSet> sourceSets
-
-    /**
-     * The configurations which files are to be transformed into classpath entries.
-     */
-    Set<Configuration> plusConfigurations = new LinkedHashSet<Configuration>()
-
-    /**
-     * The configurations which files are to be excluded from the classpath entries.
-     */
-    Set<Configuration> minusConfigurations = new LinkedHashSet<Configuration>()
-
-    /**
-     * The variables to be used for replacing absolute paths in classpath entries.
-     */
-    Map<String, File> variables = [:]
-
-    /**
-     * Containers to be added to the classpath
-     */
-    Set<Container> containers = new LinkedHashSet<Container>()
-
-    /**
-     * The default output directory for eclipse generated files, eg classes.
-     */
-    File defaultOutputDir
-
-    /**
-     * Whether to download and add sources associated with the dependency jars. Defaults to true.
-     */
-    boolean downloadSources = true
-
-    /**
-     * Whether to download and add javadocs associated with the dependency jars. Defaults to false.
-     */
-    boolean downloadJavadoc = false
-
-    protected ClasspathFactory modelFactory = new ClasspathFactory()
-
-    EclipseClasspath() {
-        xmlTransformer.indentation = "\t"
-    }
-
-    @Override protected Classpath create() {
-        return new Classpath(xmlTransformer)
-    }
-
-    @Override protected void configure(Classpath object) {
-        modelFactory.configure(this, object)
-    }
-
-    /**
-     * Adds containers to the .classpath.
-     *
-     * @param containers the container names to be added to the .classpath.
-     */
-    void containers(String... containers) {
-        assert containers != null
-        this.containers.addAll(containers as List)
-    }
-
-    /**
-     * Adds variables to be used for replacing absolute paths in classpath entries.
-     *
-     * @param variables A map where the keys are the variable names and the values are the variable values.
-     */
-    void variables(Map<String, File> variables) {
-        assert variables != null
-        this.variables.putAll variables
-    }
-}
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipseJdt.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipseJdt.groovy
deleted file mode 100644
index acce5d8..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipseJdt.groovy
+++ /dev/null
@@ -1,49 +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.plugins.eclipse
-
-import org.gradle.api.JavaVersion
-import org.gradle.api.internal.tasks.generator.PersistableConfigurationObjectGenerator
-import org.gradle.api.tasks.GeneratorTask
-import org.gradle.plugins.eclipse.model.Jdt
-
-/**
- * Generates the Eclipse JDT configuration file.
- */
-class EclipseJdt extends GeneratorTask<Jdt> {
-    /**
-     * The source Java language level.
-     */
-    JavaVersion sourceCompatibility
-
-    /**
-     * The target JVM to generate {@code .class} files for.
-     */
-    JavaVersion targetCompatibility
-
-    EclipseJdt() {
-        generator = new PersistableConfigurationObjectGenerator<Jdt>() {
-            Jdt create() {
-                return new Jdt()
-            }
-
-            void configure(Jdt jdt) {
-                jdt.sourceCompatibility = getSourceCompatibility()
-                jdt.targetCompatibility = getTargetCompatibility()
-            }
-        }
-    }
-}
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipsePlugin.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipsePlugin.groovy
deleted file mode 100644
index 6486202..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipsePlugin.groovy
+++ /dev/null
@@ -1,159 +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.plugins.eclipse
-
-import org.gradle.api.Project
-import org.gradle.api.internal.plugins.IdePlugin
-import org.gradle.api.plugins.GroovyBasePlugin
-import org.gradle.api.plugins.JavaBasePlugin
-import org.gradle.api.plugins.JavaPlugin
-import org.gradle.api.plugins.WarPlugin
-import org.gradle.api.plugins.scala.ScalaBasePlugin
-import org.gradle.plugins.eclipse.model.BuildCommand
-import org.gradle.plugins.eclipse.model.Facet
-import org.gradle.api.JavaVersion
-
-/**
- * <p>A plugin which generates Eclipse files.</p>
- *
- * @author Hans Dockter
- */
-class EclipsePlugin extends IdePlugin {
-    static final String ECLIPSE_TASK_NAME = "eclipse"
-    static final String CLEAN_ECLIPSE_TASK_NAME = "cleanEclipse"
-    static final String ECLIPSE_PROJECT_TASK_NAME = "eclipseProject"
-    static final String ECLIPSE_WTP_TASK_NAME = "eclipseWtp"
-    static final String ECLIPSE_CP_TASK_NAME = "eclipseClasspath"
-    static final String ECLIPSE_JDT_TASK_NAME = "eclipseJdt"
-
-    @Override protected String getLifecycleTaskName() {
-        return 'eclipse'
-    }
-
-    @Override protected void onApply(Project project) {
-        lifecycleTask.description = 'Generates the Eclipse files.'
-        cleanTask.description = 'Cleans the generated eclipse files.'
-        configureEclipseProject(project)
-        configureEclipseClasspath(project)
-        configureEclipseJdt(project)
-        configureEclipseWtpModuleForWarProjects(project);
-    }
-
-    private void configureEclipseProject(Project project) {
-        EclipseProject eclipseProject = project.tasks.add(ECLIPSE_PROJECT_TASK_NAME, EclipseProject.class);
-        eclipseProject.projectName = project.name
-        eclipseProject.description = "Generates the Eclipse .project file."
-        eclipseProject.inputFile = project.file('.project')
-        eclipseProject.outputFile = project.file('.project')
-        eclipseProject.conventionMapping.comment = { project.description }
-
-        project.plugins.withType(JavaBasePlugin.class) {
-            project.configure(project.eclipseProject) {
-                buildCommands = [new BuildCommand("org.eclipse.jdt.core.javabuilder")]
-                natures = ["org.eclipse.jdt.core.javanature"]
-            }
-        }
-        project.plugins.withType(GroovyBasePlugin.class) {
-            project.configure(project.eclipseProject) {
-                natures.add(natures.indexOf("org.eclipse.jdt.core.javanature"), "org.eclipse.jdt.groovy.core.groovyNature")
-            }
-        }
-        project.plugins.withType(ScalaBasePlugin.class) {
-            project.configure(project.eclipseProject) {
-                buildCommands = buildCommands.collect { command ->
-                    command.name == "org.eclipse.jdt.core.javabuilder" ? new BuildCommand("ch.epfl.lamp.sdt.core.scalabuilder") : command
-                }
-                natures.add(natures.indexOf("org.eclipse.jdt.core.javanature"), "ch.epfl.lamp.sdt.core.scalanature")
-            }
-        }
-        project.plugins.withType(WarPlugin.class) {
-            project.configure(project.eclipseProject) {
-                buildCommand 'org.eclipse.wst.common.project.facet.core.builder'
-                buildCommand 'org.eclipse.wst.validation.validationbuilder'
-                natures 'org.eclipse.wst.common.project.facet.core.nature', 'org.eclipse.wst.common.modulecore.ModuleCoreNature'
-            }
-        }
-
-        addWorker(eclipseProject)
-    }
-
-    private void configureEclipseClasspath(final Project project) {
-        project.plugins.withType(JavaBasePlugin.class) {
-            EclipseClasspath eclipseClasspath = project.tasks.add(ECLIPSE_CP_TASK_NAME, EclipseClasspath.class)
-            project.configure(eclipseClasspath) {
-                description = "Generates the Eclipse .classpath file."
-                containers 'org.eclipse.jdt.launching.JRE_CONTAINER'
-                sourceSets = project.sourceSets
-                inputFile = project.file('.classpath')
-                outputFile = project.file('.classpath')
-                conventionMapping.defaultOutputDir = { new File(project.projectDir, 'bin') }
-            }
-            addWorker(eclipseClasspath)
-        }
-        project.plugins.withType(JavaPlugin.class) {
-            project.configure(project.eclipseClasspath) {
-                plusConfigurations = [project.configurations.testRuntime]
-            }
-        }
-    }
-
-    private void configureEclipseJdt(final Project project) {
-        project.plugins.withType(JavaBasePlugin.class) {
-            EclipseJdt eclipseJdt = project.tasks.add(ECLIPSE_JDT_TASK_NAME, EclipseJdt.class)
-            project.configure(eclipseJdt) {
-                description = "Generates the Eclipse JDT settings file."
-                outputFile = project.file('.settings/org.eclipse.jdt.core.prefs')
-                inputFile = project.file('.settings/org.eclipse.jdt.core.prefs')
-                conventionMapping.sourceCompatibility = { project.sourceCompatibility }
-                conventionMapping.targetCompatibility = { project.targetCompatibility }
-            }
-            addWorker(eclipseJdt)
-        }
-    }
-
-    private void configureEclipseWtpModuleForWarProjects(final Project project) {
-        project.plugins.withType(WarPlugin.class) {
-            final EclipseWtp eclipseWtp = project.tasks.add(ECLIPSE_WTP_TASK_NAME, EclipseWtp.class);
-
-            project.configure(eclipseWtp) {
-                description = 'Generate the Eclipse WTP settings files.'
-                deployName = project.name
-                conventionMapping.contextPath = { project.war.baseName }
-                conventionMapping.facets = { [new Facet("jst.web", "2.4"), new Facet("jst.java", toJavaFacetVersion(project.sourceCompatibility))]}
-                sourceSets = project.sourceSets.matching { sourceSet -> sourceSet.name == 'main' }
-                plusConfigurations = [project.configurations.runtime]
-                minusConfigurations = [project.configurations.providedRuntime]
-                resource deployPath: '/', sourcePath: project.convention.plugins.war.webAppDirName
-                orgEclipseWstCommonComponentInputFile = project.file('.settings/org.eclipse.wst.common.component')
-                orgEclipseWstCommonComponentOutputFile = project.file('.settings/org.eclipse.wst.common.component')
-                orgEclipseWstCommonProjectFacetCoreInputFile = project.file('.settings/org.eclipse.wst.common.project.facet.core.xml')
-                orgEclipseWstCommonProjectFacetCoreOutputFile = project.file('.settings/org.eclipse.wst.common.project.facet.core.xml')
-            }
-
-            addWorker(eclipseWtp)
-        }
-    }
-
-    private String toJavaFacetVersion(JavaVersion version) {
-        if (version == JavaVersion.VERSION_1_5) {
-            return '5.0'
-        }
-        if (version == JavaVersion.VERSION_1_6) {
-            return '6.0'
-        }
-        return version.toString()
-    }
-}
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipseProject.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipseProject.groovy
deleted file mode 100644
index 709b656..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipseProject.groovy
+++ /dev/null
@@ -1,129 +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.plugins.eclipse
-
-
-import org.gradle.api.InvalidUserDataException
-import org.gradle.api.tasks.XmlGeneratorTask
-import org.gradle.plugins.eclipse.model.BuildCommand
-import org.gradle.plugins.eclipse.model.Link
-import org.gradle.plugins.eclipse.model.Project
-
-/**
- * Generates an Eclipse <code>.project</code> file.
- *
- * @author Hans Dockter
- */
-class EclipseProject extends XmlGeneratorTask<Project> {
-    private static final LINK_ARGUMENTS = ['name', 'type', 'location', 'locationUri']
-
-    /**
-     * The name used for the name of the eclipse project
-     */
-    String projectName;
-
-    /**
-     * A comment used for the eclipse project
-     */
-    String comment;
-
-    /**
-     * The referenced projects of this Eclipse project.
-     */
-    Set<String> referencedProjects = new LinkedHashSet<String>();
-
-    /**
-     * The natures to be added to this Eclipse project.
-     */
-    List<String> natures = []
-
-    /**
-     * The build commands to be added to this Eclipse project.
-     */
-    List<BuildCommand> buildCommands = []
-
-    /**
-     * The links to be added to this Eclipse project.
-     */
-    Set<Link> links = new LinkedHashSet<Link>();
-
-    EclipseProject() {
-        xmlTransformer.indentation = "\t"
-    }
-
-    @Override protected Project create() {
-        return new Project(xmlTransformer)
-    }
-
-    @Override protected void configure(Project project) {
-        project.configure(this)
-    }
-
-    /**
-     * Adds natures entries to the eclipse project.
-     * @param natures the nature names
-     */
-    void natures(String... natures) {
-        assert natures != null
-        this.natures.addAll(natures as List)
-    }
-
-    /**
-     * Adds project references to the eclipse project.
-     *
-     * @param referencedProjects The name of the project references.
-     */
-    void referencedProjects(String... referencedProjects) {
-        assert referencedProjects != null
-        this.referencedProjects.addAll(referencedProjects as List)
-    }
-
-    /**
-     * Adds a build command with arguments to the eclipse project.
-     *
-     * @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)
-     */
-    void buildCommand(Map args, String buildCommand) {
-        assert buildCommand != null
-        this.buildCommands.add(new BuildCommand(buildCommand, args))
-    }
-
-    /**
-     * Adds a build command to the eclipse project.
-     *
-     * @param buildCommand The name of the build command
-     * @see #buildCommand(Map, String)
-     */
-    void buildCommand(String buildCommand) {
-        assert buildCommand != null
-        this.buildCommands.add(new BuildCommand(buildCommand))
-    }
-
-    /**
-     * Adds a link to the eclipse project.
-     *
-     * @param args A maps with the args for the link. Legal keys for the map are name, type, location and locationUri.
-     */
-    void link(Map<String, String> args) {
-        def illegalArgs = LINK_ARGUMENTS - args.keySet()
-        if (illegalArgs) {
-            throw new InvalidUserDataException("You provided illegal argument for a link: " + illegalArgs)
-        }
-        this.links.add(new Link(args.name, args.type, args.location, args.locationUri))
-    }
-}
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipseWtp.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipseWtp.groovy
deleted file mode 100644
index 33d11b1..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipseWtp.groovy
+++ /dev/null
@@ -1,187 +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.plugins.eclipse
-
-
-import org.gradle.api.artifacts.Configuration
-import org.gradle.api.internal.ConventionTask
-import org.gradle.api.tasks.OutputFile
-import org.gradle.api.tasks.SourceSet
-import org.gradle.api.tasks.TaskAction
-import org.gradle.listener.ActionBroadcast
-import org.gradle.plugins.eclipse.model.Facet
-import org.gradle.plugins.eclipse.model.WbProperty
-import org.gradle.plugins.eclipse.model.WbResource
-import org.gradle.plugins.eclipse.model.Wtp
-import org.gradle.plugins.eclipse.model.internal.WtpFactory
-import org.gradle.util.ConfigureUtil
-
-/**
- * Generates Eclipse configuration files for Eclipse WTP.
- *
- * @author Hans Dockter
- */
-class EclipseWtp extends ConventionTask {
-    /**
-     * The file that is merged into the to be produced org.eclipse.wst.common.component file. This
-     * file must not exist.
-     */
-    File orgEclipseWstCommonComponentInputFile
-
-    /**
-     * The output file for the org.eclipse.wst.common.component metadata.
-     */
-    @OutputFile
-    File orgEclipseWstCommonComponentOutputFile
-
-    /**
-     * The file that is merged into the to be produced org.eclipse.wst.common.project.facet.core file. This
-     * file must not exist.
-     */
-    File orgEclipseWstCommonProjectFacetCoreInputFile
-
-    /**
-     * The output file for the org.eclipse.wst.common.project.facet.core metadata.
-     */
-    @OutputFile
-    File orgEclipseWstCommonProjectFacetCoreOutputFile
-
-    /**
-     * The source sets to be transformed into wb-resource elements.
-     */
-    Iterable<SourceSet> sourceSets
-
-    /**
-     * The configurations which files are to be transformed into dependent-module elements of
-     * the org.eclipse.wst.common.component file.
-     */
-    Set<Configuration> plusConfigurations
-
-    /**
-     * The configurations which files are to be excluded from the dependent-module elements of
-     * the org.eclipse.wst.common.component file.
-     */
-    Set<Configuration> minusConfigurations
-
-    /**
-     * The facets to be added as installed elements to the org.eclipse.wst.common.project.facet.core file.
-     */
-    List<Facet> facets = []
-
-    /**
-     * The deploy name to be used in the org.eclipse.wst.common.component file.
-     */
-    String deployName;
-
-    /**
-     * The variables to be used for replacing absolute path in dependent-module elements of
-     * the org.eclipse.wst.common.component file.
-     */
-    Map<String, File> variables = [:]
-
-    /**
-     * Additional wb-resource elements.
-     */
-    List<WbResource> resources = []
-
-    /**
-     * Additional property elements.
-     */
-    List<WbProperty> properties = []
-
-    /**
-     * The context path for the web application
-     */
-    String contextPath
-
-    protected WtpFactory modelFactory = new WtpFactory()
-
-    ActionBroadcast<Map<String, Node>> withXmlActions = new ActionBroadcast<Map<String, Node>>()
-    ActionBroadcast<Wtp> beforeConfiguredActions = new ActionBroadcast<Wtp>()
-    ActionBroadcast<Wtp> whenConfiguredActions = new ActionBroadcast<Wtp>()
-
-    EclipseWtp() {
-        outputs.upToDateWhen { false }
-    }
-
-    @TaskAction
-    protected void generateXml() {
-        Wtp wtp = modelFactory.createWtp(this)
-        wtp.toXml(orgEclipseWstCommonComponentOutputFile, orgEclipseWstCommonProjectFacetCoreOutputFile)
-    }
-
-    /**
-     * Adds a facet for the org.eclipse.wst.common.project.facet.core file.
-     *
-     * @param args A map that must contain a name and version key with corresponding values.
-     */
-    void facet(Map<String, ?> args) {
-        setFacets(getFacets() + [ConfigureUtil.configureByMap(args, new Facet())])
-    }
-
-    /**
-     * Adds variables to be used for replacing absolute path in dependent-module elements of
-     * the org.eclipse.wst.common.component file.
-     *
-     * @param variables A map where the keys are the variable names and the values are the variable values.
-     */
-    void variables(Map<String, File> variables) {
-        assert variables != null
-        this.variables.putAll variables
-    }
-
-    /**
-     * Adds a property to be added to the org.eclipse.wst.common.component file.
-     *
-     * @param args A map that must contain a name and value key with corresponding values.
-     */
-    void property(Map<String, String> args) {
-        properties.add(new WbProperty(args.name, args.value))
-    }
-
-    /**
-     * Adds a wb-resource to be added to the org.eclipse.wst.common.component file.
-     *
-     * @param args A map that must contain a deployPath and sourcePath key with corresponding values.
-     */
-    void resource(Map<String, String> args) {
-        resources.add(new WbResource(args.deployPath, args.sourcePath))
-    }
-
-    /**
-     * Adds a closure to be called when the XML content for each file has been generated, but before the content is
-     * written to the file.
-     */
-    void withXml(Closure closure) {
-        withXmlActions.add(closure)
-    }
-
-    /**
-     * Adds a closure to be called when the model has been loaded from the input files, and before this task has
-     * configured the model.
-     */
-    void beforeConfigured(Closure closure) {
-        beforeConfiguredActions.add(closure)
-    }
-
-    /**
-     * Adds a closure to be called after this task has configured model, and before it generates the XML content for the
-     * files.
-     */
-    void whenConfigured(Closure closure) {
-        whenConfiguredActions.add(closure)
-    }
-}
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/AbstractClasspathEntry.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/AbstractClasspathEntry.groovy
deleted file mode 100644
index 55f01d1..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/AbstractClasspathEntry.groovy
+++ /dev/null
@@ -1,146 +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.plugins.eclipse.model
-
-import org.gradle.plugins.eclipse.model.internal.PathUtil
-
-/**
- * @author Hans Dockter
- */
-abstract class AbstractClasspathEntry implements ClasspathEntry {
-    static final String NATIVE_LIBRARY_ATTRIBUTE = 'org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY'
-    String path
-    String nativeLibraryLocation
-    boolean exported
-    Set<AccessRule> accessRules
-
-    def AbstractClasspathEntry(Node node) {
-        this.path = normalizePath(node. at path)
-        this.exported = node. at exported
-        this.nativeLibraryLocation = readNativeLibraryLocation(node)
-        this.accessRules = readAccessRules(node)
-        assert path != null && accessRules != null
-    }
-
-    def AbstractClasspathEntry(String path, boolean exported, String nativeLibraryLocation, Set accessRules) {
-        assert path != null && accessRules != null
-        this.path = normalizePath(path);
-        this.exported = exported
-        this.nativeLibraryLocation = nativeLibraryLocation
-        this.accessRules = accessRules
-    }
-
-    Map removeNullEntries(Map args) {
-        def result = [:]
-        args.each { key, value ->
-            if (value) {
-                result[key] = value
-            }
-        }
-        result
-    }
-
-    Node addClasspathEntry(Node node, Map attributes) {
-        def allAttributes = removeNullEntries(attributes) + [
-                kind: getKind(),
-                path: path]
-        allAttributes += exported && !(this instanceof SourceFolder) ? [exported: exported] : [:]
-        Node entryNode = node.appendNode('classpathentry', allAttributes)
-        addNativeLibraryLocation(entryNode)
-        addAccessRules(entryNode)
-        entryNode
-    }
-
-    void addNativeLibraryLocation(Node node) {
-        addEntryAttributes(node, removeNullEntries([(NATIVE_LIBRARY_ATTRIBUTE): nativeLibraryLocation]))
-    }
-
-    String readNativeLibraryLocation(Node node) {
-        node.attributes.attribute.find { it. at name == 'org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY' }?.attributes()?.value
-    }
-
-    void addAccessRules(Node node) {
-        if (!accessRules) {
-            return
-        }
-        Node accessRulesNode = null
-        if (node.accessrules.size() == 0) {
-            accessRulesNode = node.appendNode('accessrules')
-        } else {
-            accessRulesNode = node.accessrules[0]
-        }
-        accessRules.each { AccessRule rule ->
-            accessRulesNode.appendNode('accessrule', [kind: rule.kind, pattern: rule.pattern])
-        }
-    }
-
-    def readAccessRules(Node node) {
-        node.accessrules.accessrule.collect { ruleNode ->
-            new AccessRule(ruleNode. at kind, ruleNode. at pattern)
-        }
-    }
-
-    void addEntryAttributes(Node node, Map attributes) {
-        if (!attributes) {
-            return
-        }
-        Node attributesNode = node.children().find { it.name()  == 'attributes' }
-        if (!attributesNode) {
-            attributesNode = node.appendNode('attributes')
-        }
-        attributes.each { key, value ->
-            attributesNode.appendNode('attribute', [name: key, value: value])
-        }
-    }
-
-    String normalizePath(String path) {
-        PathUtil.normalizePath(path)
-    }
-
-    boolean equals(o) {
-        if (this.is(o)) { return true }
-
-        if (getClass() != o.class) { return false }
-
-        AbstractClasspathEntry that = (AbstractClasspathEntry) o;
-
-        if (exported != that.exported) { return false }
-        if (accessRules != that.accessRules) { return false }
-        if (nativeLibraryLocation != that.nativeLibraryLocation) { return false }
-        if (path != that.path) { return false }
-
-        return true
-    }
-
-    int hashCode() {
-        int result;
-
-        result = path.hashCode();
-        result = 31 * result + (nativeLibraryLocation != null ? nativeLibraryLocation.hashCode() : 0);
-        result = 31 * result + (exported ? 1 : 0);
-        result = 31 * result + accessRules.hashCode();
-        return result;
-    }
-
-    public String toString() {
-        return "{" +
-                "path='" + path + '\'' +
-                ", nativeLibraryLocation='" + nativeLibraryLocation + '\'' +
-                ", exported=" + exported +
-                ", accessRules=" + accessRules +
-                '}';
-    }
-}
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/AbstractLibrary.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/AbstractLibrary.groovy
deleted file mode 100644
index 8058137..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/AbstractLibrary.groovy
+++ /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.plugins.eclipse.model
-
-/**
- * @author Hans Dockter
- */
-abstract class AbstractLibrary extends AbstractClasspathEntry {
-    String sourcePath
-    String javadocPath
-
-    def AbstractLibrary(node) {
-        super(node)
-        javadocPath = normalizePath(node.attributes?.attribute?.find { it. at name == 'javadoc_location' }?. at value)
-        sourcePath = normalizePath(node. at sourcepath)
-    }
-
-    def AbstractLibrary(String path, boolean exported, String nativeLibraryLocation, Set accessRules, String sourcePath,
-                        String javadocPath) {
-        super(path, exported, nativeLibraryLocation, accessRules)
-        this.sourcePath = normalizePath(sourcePath);
-        this.javadocPath = normalizePath(javadocPath);
-    }
-
-    void appendNode(Node node) {
-        def entryNode = addClasspathEntry(node, [sourcepath: sourcePath])
-        if (javadocPath) {
-            addEntryAttributes(entryNode, [javadoc_location: javadocPath])
-        }
-    }
-
-    boolean equals(o) {
-        if (this.is(o)) { return true }
-
-        if (getClass() != o.class) { return false }
-
-        AbstractLibrary that = (AbstractLibrary) o;
-
-        if (exported != that.exported) { return false }
-        if (accessRules != that.accessRules) { return false }
-        if (javadocPath != that.javadocPath) { return false }
-        if (nativeLibraryLocation != that.nativeLibraryLocation) { return false }
-        if (path != that.path) { return false }
-        if (sourcePath != that.sourcePath) { return false }
-
-        return true
-    }
-
-    int hashCode() {
-        int result;
-
-        result = path.hashCode();
-        result = 31 * result + (nativeLibraryLocation != null ? nativeLibraryLocation.hashCode() : 0);
-        result = 31 * result + (exported ? 1 : 0);
-        result = 31 * result + accessRules.hashCode();
-        result = 31 * result + (sourcePath != null ? sourcePath.hashCode() : 0);
-        result = 31 * result + (javadocPath != null ? javadocPath.hashCode() : 0);
-        return result;
-    }
-
-    public String toString() {
-        return "{" +
-                "path='" + path + '\'' +
-                ", nativeLibraryLocation='" + nativeLibraryLocation + '\'' +
-                ", exported=" + exported +
-                ", accessRules=" + accessRules +
-                ", sourcePath='" + sourcePath + '\'' +
-                ", javadocPath='" + javadocPath + '\'' +
-                '}';
-    }
-}
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/AccessRule.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/AccessRule.groovy
deleted file mode 100644
index 955e09f..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/AccessRule.groovy
+++ /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.plugins.eclipse.model
-
-/**
- * @author Hans Dockter
- */
-class AccessRule {
-    String kind
-    String pattern
-
-    def AccessRule(kind, pattern) {
-        assert kind != null && pattern != null
-        this.kind = kind;
-        this.pattern = pattern;
-    }
-
-    boolean equals(o) {
-        if (this.is(o)) { return true }
-
-        if (getClass() != o.class) { return false }
-
-        AccessRule that = (AccessRule) o;
-
-        if (kind != that.kind) { return false }
-        if (pattern != that.pattern) { return false }
-
-        return true
-    }
-
-    int hashCode() {
-        int result;
-
-        result = kind.hashCode();
-        result = 31 * result + pattern.hashCode();
-        return result;
-    }
-
-    public String toString() {
-        return "AccessRule{" +
-                "kind='" + kind + '\'' +
-                ", pattern='" + pattern + '\'' +
-                '}';
-    }
-}
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/BuildCommand.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/BuildCommand.groovy
deleted file mode 100644
index a451506..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/BuildCommand.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.plugins.eclipse.model
-
-/**
- * @author Hans Dockter
- */
-class BuildCommand {
-    String name
-    Map arguments
-
-    def BuildCommand(String name, Map arguments = [:]) {
-        assert name != null
-        assert arguments != null
-        this.name = name
-        this.arguments = arguments
-    }
-
-    boolean equals(o) {
-        if (this.is(o)) { return true }
-
-        if (getClass() != o.class) { return false }
-
-        BuildCommand that = (BuildCommand) o;
-
-        if (arguments != that.arguments) { return false }
-        if (name != that.name) { return false }
-
-        return true
-    }
-
-    int hashCode() {
-        int result;
-
-        result = name.hashCode();
-        result = 31 * result + arguments.hashCode();
-        return result;
-    }
-
-
-    public String toString() {
-        return "BuildCommand{" +
-                "name='" + name + '\'' +
-                ", arguments=" + arguments +
-                '}';
-    }
-}
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Classpath.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Classpath.groovy
deleted file mode 100644
index cb3ef01..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Classpath.groovy
+++ /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.plugins.eclipse.model
-
-import org.gradle.api.internal.XmlTransformer
-import org.gradle.api.internal.tasks.generator.XmlPersistableConfigurationObject
-
-/**
- * Represents the customizable elements of an eclipse classpath file. (via XML hooks everything is customizable).
- *
- * @author Hans Dockter
- */
-class Classpath extends XmlPersistableConfigurationObject {
-    List<ClasspathEntry> entries = []
-
-    Classpath(XmlTransformer xmlTransformer) {
-        super(xmlTransformer)
-    }
-
-    @Override protected String getDefaultResourceName() {
-        return 'defaultClasspath.xml'
-    }
-
-    @Override protected void load(Node xml) {
-        xml.classpathentry.each { Node entryNode ->
-            ClasspathEntry entry = null
-            switch (entryNode. at kind) {
-                case 'src':
-                    def path = entryNode. at path
-                    entry = path.startsWith('/') ? new ProjectDependency(entryNode) : new SourceFolder(entryNode)
-                    break
-                case 'var': entry = new Variable(entryNode)
-                    break
-                case 'con': entry = new Container(entryNode)
-                    break
-                case 'lib': entry = new Library(entryNode)
-                    break
-                case 'output': entry = new Output(entryNode)
-                    break
-            }
-            if (entry) {
-                entries.add(entry)
-            }
-        }
-    }
-
-    def configure(List newEntries) {
-        def entriesToBeKept = entries.findAll { !isDependency(it) }
-        entries = (entriesToBeKept + newEntries).unique()
-    }
-
-    @Override protected void store(Node xml) {
-        xml.classpathentry.each { xml.remove(it) }
-        entries.each { ClasspathEntry entry ->
-            entry.appendNode(xml)
-        }
-    }
-
-    boolean equals(o) {
-        if (this.is(o)) { return true }
-
-        if (getClass() != o.class) { return false }
-
-        Classpath classpath = (Classpath) o;
-
-        if (entries != classpath.entries) { return false }
-
-        return true
-    }
-
-    int hashCode() {
-        int result;
-
-        result = entries.hashCode();
-        return result;
-    }
-
-    public String toString() {
-        return "Classpath{" +
-                "entries=" + entries +
-                '}';
-    }
-
-    private boolean isDependency(ClasspathEntry entry) {
-        entry instanceof ProjectDependency || entry instanceof Library
-    }
-}
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/ClasspathEntry.java b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/ClasspathEntry.java
deleted file mode 100644
index 164aaf2..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/ClasspathEntry.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.plugins.eclipse.model;
-
-import groovy.util.Node;
-
-/**
- * Represents an entry in the Eclipse classpath.
- * 
- * @author Hans Dockter
- */
-public interface ClasspathEntry {
-    String getKind();
-    void appendNode(Node node);
-}
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Container.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Container.groovy
deleted file mode 100644
index 73a87d8..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Container.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.plugins.eclipse.model
-
-/**
- * @author Hans Dockter
- */
-class Container extends AbstractClasspathEntry {
-    def Container(node) {
-        super(node);
-    }
-
-    def Container(String path, boolean exported, String nativeLibraryLocation, Set accessRules) {
-        super(path, exported, nativeLibraryLocation, accessRules)
-    }
-
-    String getKind() {
-        'con'
-    }
-
-    void appendNode(Node node) {
-        addClasspathEntry(node, [:])
-    }
-
-    public String toString() {
-        return "Container{" + super.toString()
-    }
-}
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Facet.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Facet.groovy
deleted file mode 100644
index 9886610..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Facet.groovy
+++ /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.plugins.eclipse.model
-
-/**
- * @author Hans Dockter
- */
-
-class Facet {
-    String name
-    String version
-
-    def Facet() {
-    }
-
-    def Facet(Node node) {
-        this(node. at facet, node. at version)
-    }
-
-    def Facet(String name, String version) {
-        assert name != null && version != null
-        this.name = name
-        this.version = version
-    }
-
-    void appendNode(Node node) {
-        node.appendNode("installed", [facet: name, version: version])
-    }
-
-    boolean equals(o) {
-        if (this.is(o)) { return true }
-
-        if (getClass() != o.class) { return false }
-
-        Facet facet = (Facet) o;
-
-        if (name != facet.name) { return false }
-        if (version != facet.version) { return false }
-
-        return true
-    }
-
-    int hashCode() {
-        int result;
-
-        result = name.hashCode();
-        result = 31 * result + version.hashCode();
-        return result;
-    }
-
-    public String toString() {
-        return "Facet{" +
-                "name='" + name + '\'' +
-                ", version='" + version + '\'' +
-                '}';
-    }
-}
\ No newline at end of file
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Jdt.java b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Jdt.java
deleted file mode 100644
index 9df6936..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Jdt.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.plugins.eclipse.model;
-
-import org.gradle.api.JavaVersion;
-import org.gradle.api.internal.tasks.generator.PropertiesPersistableConfigurationObject;
-
-import java.util.Properties;
-
-/**
- * Represents the Eclipse JDT settings.
- */
-public class Jdt extends PropertiesPersistableConfigurationObject {
-    private JavaVersion sourceCompatibility;
-    private JavaVersion targetCompatibility;
-
-    /**
-     * Sets the source compatibility for the compiler.
-     */
-    public void setSourceCompatibility(JavaVersion sourceCompatibility) {
-        this.sourceCompatibility = sourceCompatibility;
-    }
-
-    /**
-     * Sets the target compatibility for the compiler.
-     */
-    public void setTargetCompatibility(JavaVersion targetCompatibility) {
-        this.targetCompatibility = targetCompatibility;
-    }
-
-    @Override
-    protected String getDefaultResourceName() {
-        return "defaultJdtPrefs.properties";
-    }
-
-    @Override
-    protected void load(Properties properties) {
-    }
-
-    @Override
-    protected void store(Properties properties) {
-        properties.put("org.eclipse.jdt.core.compiler.compliance", sourceCompatibility.toString());
-        properties.put("org.eclipse.jdt.core.compiler.source", sourceCompatibility.toString());
-
-        if (sourceCompatibility.compareTo(JavaVersion.VERSION_1_3) <= 0) {
-            properties.put("org.eclipse.jdt.core.compiler.problem.assertIdentifier", "ignore");
-            properties.put("org.eclipse.jdt.core.compiler.problem.enumIdentifier", "ignore");
-        } else if (sourceCompatibility == JavaVersion.VERSION_1_4) {
-            properties.put("org.eclipse.jdt.core.compiler.problem.assertIdentifier", "error");
-            properties.put("org.eclipse.jdt.core.compiler.problem.enumIdentifier", "warning");
-        } else {
-            properties.put("org.eclipse.jdt.core.compiler.problem.assertIdentifier", "error");
-            properties.put("org.eclipse.jdt.core.compiler.problem.enumIdentifier", "error");
-        }
-
-        properties.put("org.eclipse.jdt.core.compiler.codegen.targetPlatform", targetCompatibility.toString());
-    }
-}
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Library.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Library.groovy
deleted file mode 100644
index 109501e..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Library.groovy
+++ /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.plugins.eclipse.model
-
-/**
- * @author Hans Dockter
- */
-class Library extends AbstractLibrary {
-    def Library(Node node) {
-        super(node);
-    }
-
-    def Library(String path, boolean exported, String nativeLibraryLocation, Set accessRules, String sourcePath, String javadocPath) {
-        super(path, exported, nativeLibraryLocation, accessRules, sourcePath, javadocPath)
-    }
-
-    String getKind() {
-        'lib'
-    }
-
-    public String toString() {
-        return "Library{" + super.toString()
-    }
-}
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Link.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Link.groovy
deleted file mode 100644
index 36a76f6..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Link.groovy
+++ /dev/null
@@ -1,65 +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.plugins.eclipse.model
-
-import org.gradle.plugins.eclipse.model.internal.PathUtil
-
-/**
- * @author Hans Dockter
- */
-class Link {
-    String name
-    String type
-    String location
-    String locationUri
-
-    def Link(String name, String type, String location, String locationUri) {
-        assert name
-        assert type
-        assert location || locationUri
-        assert !location || !locationUri 
-        this.name = name;
-        this.type = type;
-        this.location = PathUtil.normalizePath(location);
-        this.locationUri = locationUri;
-    }
-
-
-    boolean equals(o) {
-        if (this.is(o)) { return true }
-
-        if (getClass() != o.class) { return false }
-
-        Link link = (Link) o;
-
-        if (location != link.location) { return false }
-        if (locationUri != link.locationUri) { return false }
-        if (name != link.name) { return false }
-        if (type != link.type) { return false }
-
-        return true
-    }
-
-    int hashCode() {
-        int result;
-
-        result = name.hashCode();
-        result = 31 * result + type.hashCode();
-        result = 31 * result + (location != null ? location.hashCode() : 0);
-        result = 31 * result + (locationUri != null ? locationUri.hashCode() : 0);
-        return result;
-    }
-}
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Output.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Output.groovy
deleted file mode 100644
index fa26b29..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Output.groovy
+++ /dev/null
@@ -1,65 +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.plugins.eclipse.model
-
-import org.gradle.plugins.eclipse.model.internal.PathUtil
-
-/**
- * @author Hans Dockter
- */
-
-class Output implements ClasspathEntry {
-    String path
-
-    def Output(Node node) {
-        this(node. at path)
-    }
-
-    def Output(String path) {
-        assert path != null
-        this.path = PathUtil.normalizePath(path)
-    }
-
-    String getKind() {
-        'output'
-    }
-
-    void appendNode(Node node) {
-        node.appendNode('classpathentry', [kind: getKind(), path: path])
-    }
-
-    boolean equals(o) {
-        if (this.is(o)) { return true }
-
-        if (getClass() != o.class) { return false }
-
-        Output output = (Output) o;
-
-        if (path != output.path) { return false }
-
-        return true
-    }
-
-    int hashCode() {
-        return path.hashCode();
-    }
-
-    public String toString() {
-        return "Output{" +
-                "path='" + path + '\'' +
-                '}';
-    }
-}
\ No newline at end of file
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Project.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Project.groovy
deleted file mode 100644
index b1f54dc..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Project.groovy
+++ /dev/null
@@ -1,216 +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.plugins.eclipse.model
-
-import org.gradle.api.internal.XmlTransformer
-import org.gradle.plugins.eclipse.EclipseProject
-import org.gradle.api.internal.tasks.generator.XmlPersistableConfigurationObject
-
-/**
- * Represents the customizable elements of an eclipse project file. (via XML hooks everything is customizable).
- *
- * @author Hans Dockter
- */
-class Project extends XmlPersistableConfigurationObject {
-    public static final String PROJECT_FILE_NAME = ".project";
-
-    /**
-     * The name used for the name of the eclipse project
-     */
-    String name;
-
-    /**
-     * A comment used for the eclipse project
-     */
-    String comment;
-
-    /**
-     * The referenced projects of this Eclipse project.
-     */
-    Set<String> referencedProjects = new LinkedHashSet<String>()
-
-    /**
-     * The natures to be added to this Eclipse project.
-     */
-    List<String> natures = []
-
-    /**
-     * The build commands to be added to this Eclipse project.
-     */
-    List buildCommands = []
-
-    /**
-     * The links to be added to this Eclipse project.
-     */
-    Set<Link> links = new LinkedHashSet<Link>()
-
-    def Project(XmlTransformer xmlTransformer) {
-        super(xmlTransformer)
-    }
-
-    @Override protected String getDefaultResourceName() {
-        return 'defaultProject.xml'
-    }
-
-    @Override protected void load(Node xml) {
-        this.name = xml.name.text()
-        this.comment = xml.comment.text()
-        readReferencedProjects()
-        readNatures()
-        readBuildCommands()
-        readLinks()
-    }
-
-    private def readReferencedProjects() {
-        return xml.projects.project.each {
-            this.referencedProjects.add(it.text())
-        }
-    }
-
-    private def readNatures() {
-        return xml.natures.nature.each { this.natures.add(it.text()) }
-    }
-
-    private def readBuildCommands() {
-        return xml.buildSpec.buildCommand.each { command ->
-            Map args = [:]
-            command.arguments.dictionary.each { Node it ->
-                args[it.key.text()] = it.value.text()
-            }
-            this.buildCommands.add(new BuildCommand(command.name.text(), args))
-        }
-    }
-
-    private def readLinks() {
-        return xml.links.link.each { link ->
-            this.links.add(new Link(link.name?.text(), link.type?.text(), link.location?.text(), link.locationURI?.text()))
-        }
-    }
-
-    def configure(EclipseProject eclipseProjectTask) {
-        if (eclipseProjectTask.projectName) {
-            this.name = eclipseProjectTask.projectName
-        }
-        if (eclipseProjectTask.comment) {
-            this.comment = eclipseProjectTask.comment
-        }
-        this.referencedProjects.addAll(eclipseProjectTask.referencedProjects)
-        this.natures.addAll(eclipseProjectTask.natures)
-        this.natures.unique()
-        this.buildCommands.addAll(eclipseProjectTask.buildCommands)
-        this.buildCommands.unique()
-        this.links.addAll(eclipseProjectTask.links);
-    }
-
-    @Override protected void store(Node xml) {
-        ['name', 'comment', 'projects', 'natures', 'buildSpec', 'links'].each { childNodeName ->
-            Node childNode = xml.children().find { it.name() == childNodeName }
-            if (childNode) {
-                xml.remove(childNode)
-            }
-        }
-        xml.appendNode('name', this.name)
-        xml.appendNode('comment', this.comment ?: null)
-        addReferencedProjectsToXml()
-        addNaturesToXml()
-        addBuildSpecToXml()
-        addLinksToXml()
-    }
-
-    private def addReferencedProjectsToXml() {
-        def referencedProjectsNode = xml.appendNode('projects')
-        this.referencedProjects.each { projectName ->
-            referencedProjectsNode.appendNode('project', projectName)
-        }
-    }
-
-    private def addNaturesToXml() {
-        def naturesNode = xml.appendNode('natures')
-        this.natures.each { nature ->
-            naturesNode.appendNode('nature', nature)
-        }
-    }
-
-    private def addBuildSpecToXml() {
-        def buildSpec = xml.appendNode('buildSpec')
-        this.buildCommands.each { command ->
-            def commandNode = buildSpec.appendNode('buildCommand')
-            commandNode.appendNode('name', command.name)
-            def argumentsNode = commandNode.appendNode('arguments')
-            command.arguments.each { key, value ->
-                def dictionaryNode = argumentsNode.appendNode('dictionary')
-                dictionaryNode.appendNode('key', key)
-                dictionaryNode.appendNode('value', value)
-            }
-        }
-    }
-
-    private def addLinksToXml() {
-        def linksNode = xml.appendNode('links')
-        this.links.each { link ->
-            def linkNode = linksNode.appendNode('link')
-            linkNode.appendNode('name', link.name)
-            linkNode.appendNode('type', link.type)
-            if (link.location) {
-                linkNode.appendNode('location', link.location)
-            }
-            if (link.locationUri) {
-                linkNode.appendNode('locationURI', link.locationUri)
-            }
-        }
-    }
-
-    boolean equals(o) {
-        if (this.is(o)) { return true }
-
-        if (getClass() != o.class) { return false }
-
-        Project project = (Project) o;
-
-        if (buildCommands != project.buildCommands) { return false }
-        if (comment != project.comment) { return false }
-        if (links != project.links) { return false }
-        if (name != project.name) { return false }
-        if (natures != project.natures) { return false }
-        if (referencedProjects != project.referencedProjects) { return false }
-
-        return true
-    }
-
-    int hashCode() {
-        int result;
-
-        result = (name != null ? name.hashCode() : 0);
-        result = 31 * result + (comment != null ? comment.hashCode() : 0);
-        result = 31 * result + (referencedProjects != null ? referencedProjects.hashCode() : 0);
-        result = 31 * result + (natures != null ? natures.hashCode() : 0);
-        result = 31 * result + (buildCommands != null ? buildCommands.hashCode() : 0);
-        result = 31 * result + (links != null ? links.hashCode() : 0);
-        return result;
-    }
-
-
-    public String toString() {
-        return "Project{" +
-                "name='" + name + '\'' +
-                ", comment='" + comment + '\'' +
-                ", referencedProjects=" + referencedProjects +
-                ", natures=" + natures +
-                ", buildCommands=" + buildCommands +
-                ", links=" + links +
-                '}';
-    }
-}
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/ProjectDependency.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/ProjectDependency.groovy
deleted file mode 100644
index 4440c9a..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/ProjectDependency.groovy
+++ /dev/null
@@ -1,47 +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.plugins.eclipse.model
-
-/**
- * @author Hans Dockter
- */
-class ProjectDependency extends AbstractClasspathEntry {
-    def ProjectDependency(Node node) {
-        super(node)
-        assertPathIsValid()
-    }
-
-    def ProjectDependency(String path, boolean exported, String nativeLibraryLocation, Set accessRules) {
-        super(path, exported, nativeLibraryLocation, accessRules)
-        assertPathIsValid()
-    }
-
-    void assertPathIsValid() {
-        assert path.startsWith('/')
-    }
-
-    String getKind() {
-        'src'
-    }
-
-    void appendNode(Node node) {
-        addClasspathEntry(node, [:])
-    }
-
-    public String toString() {
-        return "ProjectDependency{" + super.toString()
-    }
-}
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/SourceFolder.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/SourceFolder.groovy
deleted file mode 100644
index 78eae82..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/SourceFolder.groovy
+++ /dev/null
@@ -1,92 +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.plugins.eclipse.model
-
-/**
- * @author Hans Dockter
- */
-class SourceFolder extends AbstractClasspathEntry {
-    String output
-    List excludes
-    List includes
-
-    def SourceFolder(Node node) {
-        super(node)
-        this.output = normalizePath(node. at output)
-        this.includes = node. at including?.split('\\|') ?: []
-        this.excludes = node. at excluding?.split('\\|') ?: []
-    }
-
-    def SourceFolder(String path, String nativeLibraryLocation, Set accessRules, String output,
-                     List includes, List excludes) {
-        super(path, false, nativeLibraryLocation, accessRules)
-        this.output = normalizePath(output);
-        this.includes = includes ?: [];
-        this.excludes = excludes ?: [];
-    }
-
-    String getKind() {
-        'src'
-    }
-
-    void appendNode(Node node) {
-        addClasspathEntry(node, [including: includes.join("|"), excluding: excludes.join("|"), output: output])
-    }
-
-    boolean equals(o) {
-        if (this.is(o)) { return true }
-
-        if (getClass() != o.class) { return false }
-
-        SourceFolder that = (SourceFolder) o;
-
-        if (exported != that.exported) { return false }
-        if (accessRules != that.accessRules) { return false }
-        if (excludes != that.excludes) { return false }
-        if (includes != that.includes) { return false }
-        if (nativeLibraryLocation != that.nativeLibraryLocation) { return false }
-        if (output != that.output) { return false }
-        if (path != that.path) { return false }
-
-        return true
-    }
-
-    int hashCode() {
-        int result;
-
-        result = path.hashCode();
-        result = 31 * result + (nativeLibraryLocation != null ? nativeLibraryLocation.hashCode() : 0);
-        result = 31 * result + (exported ? 1 : 0);
-        result = 31 * result + accessRules.hashCode();
-        result = 31 * result + (output != null ? output.hashCode() : 0);
-        result = 31 * result + excludes.hashCode();
-        result = 31 * result + includes.hashCode();
-        return result;
-    }
-
-
-    public String toString() {
-        return "SourceFolder{" +
-                "path='" + path + '\'' +
-                ", nativeLibraryLocation='" + nativeLibraryLocation + '\'' +
-                ", exported=" + exported +
-                ", accessRules=" + accessRules +
-                ", output='" + output + '\'' +
-                ", excludes=" + excludes +
-                ", includes=" + includes +
-                '}';
-    }
-}
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Variable.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Variable.groovy
deleted file mode 100644
index 676916f..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Variable.groovy
+++ /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.plugins.eclipse.model
-
-/**
- * @author Hans Dockter
- */
-class Variable extends AbstractLibrary {
-    def Variable(Node node) {
-        super(node);
-    }
-
-    def Variable(String path, boolean exported, String nativeLibraryLocation, Set accessRules, String sourcePath, String javadocPath) {
-        super(path, exported, nativeLibraryLocation, accessRules, sourcePath, javadocPath)
-    }
-
-    String getKind() {
-        'var'
-    }
-
-    public String toString() {
-        return "Variable{" + super.toString()
-    }
-}
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/WbDependentModule.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/WbDependentModule.groovy
deleted file mode 100644
index 6dba906..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/WbDependentModule.groovy
+++ /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.plugins.eclipse.model
-
-import org.gradle.plugins.eclipse.model.internal.PathUtil
-
-/**
- * @author Hans Dockter
- */
-
-class WbDependentModule {
-    String deployPath
-    String handle
-
-    def WbDependentModule(node) {
-        this(node.@'deploy-path', node. at handle)
-    }
-
-    def WbDependentModule(String deployPath, String handle) {
-        assert deployPath != null && handle != null
-        this.deployPath = PathUtil.normalizePath(deployPath)
-        this.handle = handle
-    }
-
-    void appendNode(Node parentNode) {
-        Node node = parentNode.appendNode("dependent-module", ['deploy-path': deployPath, 'handle': handle])
-        node.appendNode('dependency-type').setValue('uses')
-    }
-
-    boolean equals(o) {
-        if (this.is(o)) { return true }
-
-        if (getClass() != o.class) { return false }
-
-        WbDependentModule that = (WbDependentModule) o;
-
-        if (deployPath != that.deployPath) { return false }
-        if (handle != that.handle) { return false }
-
-        return true
-    }
-
-    int hashCode() {
-        int result;
-
-        result = deployPath.hashCode();
-        result = 31 * result + handle.hashCode();
-        return result;
-    }
-
-    public String toString() {
-        return "WbDependentModule{" +
-                "deployPath='" + deployPath + '\'' +
-                ", handle='" + handle + '\'' +
-                '}';
-    }
-}
\ No newline at end of file
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/WbProperty.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/WbProperty.groovy
deleted file mode 100644
index 948bc92..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/WbProperty.groovy
+++ /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.plugins.eclipse.model
-
-/**
- * @author Hans Dockter
- */
-
-class WbProperty {
-    String name
-    String value
-
-    def WbProperty(node) {
-        this(node. at name, node. at value)
-    }
-
-    def WbProperty(String name, String value) {
-        assert name != null && value != null
-        this.name = name
-        this.value = value
-    }
-
-    void appendNode(Node node) {
-        node.appendNode("property", [name: name, value: value])
-    }
-
-    boolean equals(o) {
-        if (this.is(o)) { return true }
-
-        if (getClass() != o.class) { return false }
-
-        WbProperty that = (WbProperty) o;
-
-        if (name != that.name) { return false }
-        if (value != that.value) { return false }
-
-        return true
-    }
-
-    int hashCode() {
-        int result;
-
-        result = name.hashCode();
-        result = 31 * result + value.hashCode();
-        return result;
-    }
-
-    public String toString() {
-        return "WbProperty{" +
-                "name='" + name + '\'' +
-                ", value='" + value + '\'' +
-                '}';
-    }
-}
\ No newline at end of file
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/WbResource.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/WbResource.groovy
deleted file mode 100644
index 7e586e3..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/WbResource.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.plugins.eclipse.model
-
-import org.gradle.plugins.eclipse.model.internal.PathUtil
-
-/**
- * @author Hans Dockter
- */
-
-class WbResource {
-    String deployPath
-    String sourcePath
-
-    def WbResource(node) {
-        this(node.@'deploy-path', node.@'source-path')
-    }
-
-    def WbResource(String deployPath, String sourcePath) {
-        assert deployPath != null && sourcePath != null
-        this.deployPath = PathUtil.normalizePath(deployPath)
-        this.sourcePath = PathUtil.normalizePath(sourcePath)
-    }
-
-    void appendNode(Node node) {
-        node.appendNode("wb-resource", ['deploy-path': deployPath, 'source-path': sourcePath])
-    }
-
-    boolean equals(o) {
-        if (this.is(o)) { return true }
-
-        if (getClass() != o.class) { return false }
-
-        WbResource that = (WbResource) o;
-
-        if (deployPath != that.deployPath) { return false }
-        if (sourcePath != that.sourcePath) { return false }
-
-        return true
-    }
-
-    int hashCode() {
-        int result;
-
-        result = deployPath.hashCode();
-        result = 31 * result + sourcePath.hashCode();
-        return result;
-    }
-
-    public String toString() {
-        return "WbResource{" +
-                "deployPath='" + deployPath + '\'' +
-                ", sourcePath='" + sourcePath + '\'' +
-                '}';
-    }
-}
\ No newline at end of file
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Wtp.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Wtp.groovy
deleted file mode 100644
index 2a9ea14..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Wtp.groovy
+++ /dev/null
@@ -1,184 +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.plugins.eclipse.model
-
-import org.gradle.api.Action
-import org.gradle.plugins.eclipse.EclipseWtp
-
-/**
- * @author Hans Dockter
- */
-class Wtp {
-    List wbModuleEntries = []
-
-    List facets = []
-
-    String deployName
-
-    String contextPath
-
-    private Node orgEclipseWstCommonComponentXml
-    private Node orgEclipseWstCommonProjectFacetCoreXml
-
-    private Action<Map<String, Node>> withXmlActions
-
-    Wtp(EclipseWtp eclipseWtp, List wbModuleEntries, Reader inputOrgEclipseWstCommonComponentXml,
-        Reader inputOrgEclipseWstCommonProjectFacetCoreXml) {
-        initFromXml(inputOrgEclipseWstCommonComponentXml, inputOrgEclipseWstCommonProjectFacetCoreXml)
-
-        eclipseWtp.beforeConfiguredActions.execute(this)
-
-        this.wbModuleEntries.addAll(wbModuleEntries)
-        this.wbModuleEntries.unique()
-        this.facets.addAll(eclipseWtp.facets)
-        this.facets.unique()
-        if (eclipseWtp.deployName) {
-            this.deployName = eclipseWtp.deployName
-        }
-        if (eclipseWtp.contextPath) {
-            this.contextPath = eclipseWtp.contextPath
-        }
-        this.withXmlActions = eclipseWtp.withXmlActions
-
-        eclipseWtp.whenConfiguredActions.execute(this)
-    }
-
-    private def initFromXml(Reader inputOrgEclipseWstCommonComponentXml, Reader inputOrgEclipseWstCommonProjectFacetCoreXml) {
-        if (!inputOrgEclipseWstCommonComponentXml) {
-            orgEclipseWstCommonComponentXml =
-                new Node(null, 'project-modules', [id: "moduleCoreId", 'project-version': "2.0"])
-            orgEclipseWstCommonComponentXml.appendNode('wb-module')
-            orgEclipseWstCommonProjectFacetCoreXml = new Node(null, 'faceted-project')
-            orgEclipseWstCommonProjectFacetCoreXml.appendNode('fixed', [facet: 'jst.java'])
-            orgEclipseWstCommonProjectFacetCoreXml.appendNode('fixed', [facet: 'jst.web'])
-            return
-        }
-
-        orgEclipseWstCommonComponentXml = readOrgEclipseWstCommonComponentXml(inputOrgEclipseWstCommonComponentXml)
-        orgEclipseWstCommonProjectFacetCoreXml = readOrgEclipseWstCommonProjectFacetCoreXml(inputOrgEclipseWstCommonProjectFacetCoreXml)
-    }
-
-    private def readOrgEclipseWstCommonComponentXml(Reader inputXml) {
-        def rootNode = new XmlParser().parse(inputXml)
-
-        deployName = rootNode.'wb-module'[0].@'deploy-name'
-        rootNode.'wb-module'[0].children().each { entryNode ->
-            def entry = null
-            switch (entryNode.name()) {
-                case 'property':
-                    if (entryNode. at name == 'context-root') {
-                        contextPath = entryNode. at value
-                    } else {
-                        entry = new WbProperty(entryNode)
-                    }
-                    break
-                case 'wb-resource': entry = new WbResource(entryNode)
-                    break
-                case 'dependent-module': entry = new WbDependentModule(entryNode)
-                    break
-            }
-            if (entry) {
-                wbModuleEntries.add(entry)
-            }
-        }
-        rootNode
-    }
-
-    private def readOrgEclipseWstCommonProjectFacetCoreXml(Reader inputXml) {
-        def rootNode = new XmlParser().parse(inputXml)
-
-        rootNode.installed.each { entryNode ->
-            facets.add(new Facet(entryNode))
-        }
-        rootNode
-    }
-
-    void toXml(File orgEclipseWstCommonComponentXmlFile, File orgEclipseWstCommonProjectFacetCoreXmlFile) {
-        orgEclipseWstCommonComponentXmlFile.withWriter {Writer componentWriter ->
-            orgEclipseWstCommonProjectFacetCoreXmlFile.withWriter {Writer facetWriter ->
-                toXml(componentWriter, facetWriter)
-            }
-        }
-    }
-
-    def toXml(Writer orgEclipseWstCommonComponentXmlWriter, Writer orgEclipseWstCommonProjectFacetCoreXmlWriter) {
-        removeConfigurableDataFromXml()
-        orgEclipseWstCommonComponentXml.'wb-module'[0].@'deploy-name' = deployName
-        new WbProperty('context-root', contextPath).appendNode(orgEclipseWstCommonComponentXml.'wb-module')
-        wbModuleEntries.each { entry ->
-            entry.appendNode(orgEclipseWstCommonComponentXml.'wb-module')
-        }
-        facets.each { facet ->
-            facet.appendNode(orgEclipseWstCommonProjectFacetCoreXml)
-        }
-        withXmlActions.execute([
-                'org.eclipse.wst.commons.component': orgEclipseWstCommonComponentXml,
-                'org.eclipse.wst.commons.project.facet.core': orgEclipseWstCommonProjectFacetCoreXml])
-
-        printNode(orgEclipseWstCommonComponentXml, orgEclipseWstCommonComponentXmlWriter)
-        printNode(orgEclipseWstCommonProjectFacetCoreXml, orgEclipseWstCommonProjectFacetCoreXmlWriter)
-    }
-
-    private void removeConfigurableDataFromXml() {
-        ['property', 'wb-resource', 'dependent-module'].each { elementName ->
-            orgEclipseWstCommonComponentXml.'wb-module'."$elementName".each { elementNode ->
-                orgEclipseWstCommonComponentXml.'wb-module'[0].remove(elementNode)
-            }
-        }
-        orgEclipseWstCommonProjectFacetCoreXml.installed.each { orgEclipseWstCommonProjectFacetCoreXml.remove(it) }
-    }
-
-    private void printNode(Node node, Writer writer) {
-        def printWriter = new PrintWriter(writer)
-        def nodePrinter = new XmlNodePrinter(printWriter, "\t")
-        nodePrinter.preserveWhitespace = true
-        nodePrinter.print(node)
-        printWriter.flush()
-    }
-
-    boolean equals(o) {
-        if (this.is(o)) { return true }
-
-        if (getClass() != o.class) { return false }
-
-        Wtp wtp = (Wtp) o;
-
-        if (deployName != wtp.deployName) { return false }
-        if (contextPath != wtp.contextPath) { return false }
-        if (facets != wtp.facets) { return false }
-        if (wbModuleEntries != wtp.wbModuleEntries) { return false }
-
-        return true
-    }
-
-    int hashCode() {
-        int result;
-
-        result = wbModuleEntries.hashCode();
-        result = 31 * result + facets.hashCode();
-        result = 31 * result + deployName.hashCode();
-        return result;
-    }
-
-    public String toString() {
-        return "Wtp{" +
-                "wbModuleEntries=" + wbModuleEntries +
-                ", facets=" + facets +
-                ", deployName='" + deployName + '\'' +
-                ", contextPath='" + contextPath + '\'' +
-                '}';
-    }
-}
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/internal/ClasspathFactory.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/internal/ClasspathFactory.groovy
deleted file mode 100644
index 55b8a27..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/internal/ClasspathFactory.groovy
+++ /dev/null
@@ -1,207 +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.plugins.eclipse.model.internal
-
-import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
-import org.gradle.api.specs.Specs
-import org.gradle.api.tasks.SourceSet
-import org.gradle.api.artifacts.*
-import org.gradle.plugins.eclipse.model.*
-import org.gradle.plugins.eclipse.EclipseClasspath
-
-/**
- * @author Hans Dockter
- */
-class ClasspathFactory {
-    def configure(EclipseClasspath eclipseClasspath, Classpath classpath) {
-        List entries = []
-        entries.add(new Output(eclipseClasspath.project.relativePath(eclipseClasspath.defaultOutputDir)))
-        entries.addAll(getEntriesFromSourceSets(eclipseClasspath.sourceSets, eclipseClasspath.project))
-        entries.addAll(getEntriesFromContainers(eclipseClasspath.getContainers()))
-        entries.addAll(getEntriesFromConfigurations(eclipseClasspath))
-        classpath.configure(entries)
-    }
-
-    List getEntriesFromSourceSets(def sourceSets, def project) {
-        List entries = []
-        def sortedSourceSets = sortSourceSetsAsPerUsualConvention(sourceSets.all)
-
-        sortedSourceSets.each { SourceSet sourceSet ->
-            def sourceDirSets = sourceSet.allSource.sourceTrees
-            def sourceDirs = sourceDirSets.collect { it.srcDirs }.flatten()
-            def sortedSourceDirs = sortSourceDirsAsPerUsualConvention(sourceDirs)
-
-            sortedSourceDirs.each { dir ->
-                if (dir.isDirectory()) {
-                    def sourceDirSet = sourceDirSets.find { it.srcDirs.contains(dir) }
-                    entries.add(new SourceFolder(
-                            project.relativePath(dir),
-                            null,
-                            [] as Set,
-                            null,
-                            sourceDirSet.includes as List,
-                            sourceDirSet.excludes as List))
-                }
-            }
-        }
-        entries
-    }
-
-    List getEntriesFromContainers(Set containers) {
-        containers.collect { container ->
-            new Container(container, true, null, [] as Set)
-        }
-    }
-
-    List getEntriesFromConfigurations(EclipseClasspath eclipseClasspath) {
-        getModules(eclipseClasspath) + getLibraries(eclipseClasspath)
-    }
-
-    protected List getModules(EclipseClasspath eclipseClasspath) {
-        return getDependencies(eclipseClasspath.plusConfigurations, eclipseClasspath.minusConfigurations, { it instanceof org.gradle.api.artifacts.ProjectDependency }).collect { projectDependency ->
-            projectDependency.dependencyProject
-        }.collect { dependencyProject ->
-            new org.gradle.plugins.eclipse.model.ProjectDependency('/' + dependencyProject.name, true, null, [] as Set)
-        }
-    }
-
-    protected Set getLibraries(EclipseClasspath eclipseClasspath) {
-        Set declaredDependencies = getDependencies(eclipseClasspath.plusConfigurations, eclipseClasspath.minusConfigurations,
-                { it instanceof ExternalDependency})
-
-        ResolvedConfiguration resolvedConfiguration = eclipseClasspath.project.configurations.
-                detachedConfiguration((declaredDependencies as Dependency[])).resolvedConfiguration
-        def allResolvedDependencies = getAllDeps(resolvedConfiguration.firstLevelModuleDependencies)
-
-        Set sourceDependencies = getResolvableDependenciesForAllResolvedDependencies(allResolvedDependencies) { dependency ->
-            addSourceArtifact(dependency)
-        }
-        Map sourceFiles = eclipseClasspath.downloadSources ? getFiles(eclipseClasspath.project, sourceDependencies, "sources") : [:]
-
-        Set javadocDependencies = getResolvableDependenciesForAllResolvedDependencies(allResolvedDependencies) { dependency ->
-            addJavadocArtifact(dependency)
-        }
-        Map javadocFiles = eclipseClasspath.downloadJavadoc ? getFiles(eclipseClasspath.project, javadocDependencies, "javadoc") : [:]
-
-        List moduleLibraries = resolvedConfiguration.getFiles(Specs.SATISFIES_ALL).collect { File binaryFile ->
-            File sourceFile = sourceFiles[binaryFile.name]
-            File javadocFile = javadocFiles[binaryFile.name]
-            createLibraryEntry(binaryFile, sourceFile, javadocFile, eclipseClasspath.variables)
-        }
-        moduleLibraries.addAll(getSelfResolvingFiles(getDependencies(eclipseClasspath.plusConfigurations, eclipseClasspath.minusConfigurations,
-                { it instanceof SelfResolvingDependency && !(it instanceof org.gradle.api.artifacts.ProjectDependency)}), eclipseClasspath.variables))
-        moduleLibraries
-    }
-
-    private def getSelfResolvingFiles(Collection dependencies, Map<String, File> variables) {
-        dependencies.inject([] as LinkedHashSet) { result, SelfResolvingDependency selfResolvingDependency ->
-            result.addAll(selfResolvingDependency.resolve().collect { File file ->
-                createLibraryEntry(file, null, null, variables)
-            })
-            result
-        }
-    }
-
-    AbstractLibrary createLibraryEntry(File binary, File source, File javadoc, Map<String, File> variables) {
-        def usedVariableEntry = variables.find { String name, File value -> binary.canonicalPath.startsWith(value.canonicalPath) }
-        if (usedVariableEntry) {
-            String name = usedVariableEntry.key
-            String value = usedVariableEntry.value.canonicalPath
-            String binaryPath = name + binary.canonicalPath.substring(value.length())
-            String sourcePath = source ? name + source.canonicalPath.substring(value.length()) : null
-            String javadocPath = javadoc ? name + javadoc.canonicalPath.substring(value.length()) : null
-            return new Variable(binaryPath, true, null, [] as Set, sourcePath, javadocPath)
-        }
-        new Library(binary.canonicalPath, true, null, [] as Set, source ? source.canonicalPath : null, javadoc ? javadoc.canonicalPath : null)
-    }
-
-    private Set getDependencies(Set plusConfigurations, Set minusConfigurations, Closure filter) {
-        Set declaredDependencies = new LinkedHashSet()
-        plusConfigurations.each { configuration ->
-            declaredDependencies.addAll(configuration.getAllDependencies().findAll(filter))
-        }
-        minusConfigurations.each { configuration ->
-            configuration.getAllDependencies().findAll(filter).each { minusDep ->
-                declaredDependencies.remove(minusDep)
-            }
-        }
-        return declaredDependencies
-    }
-
-    private def getFiles(def project, Set dependencies, String classifier) {
-        return project.configurations.detachedConfiguration((dependencies as Dependency[])).files.inject([:]) { result, sourceFile ->
-            String key = sourceFile.name.replace("-${classifier}.jar", '.jar')
-            result[key] = sourceFile
-            result
-        }
-    }
-
-    private List getResolvableDependenciesForAllResolvedDependencies(Set allResolvedDependencies, Closure configureClosure) {
-        return allResolvedDependencies.collect { ResolvedDependency resolvedDependency ->
-            def dependency = new DefaultExternalModuleDependency(resolvedDependency.moduleGroup, resolvedDependency.moduleName, resolvedDependency.moduleVersion,
-                    resolvedDependency.configuration)
-            dependency.transitive = false
-            configureClosure.call(dependency)
-            dependency
-        }
-    }
-
-    private List<SourceSet> sortSourceSetsAsPerUsualConvention(Collection<SourceSet> sourceSets) {
-        return sourceSets.sort { sourceSet ->
-            switch(sourceSet.name) {
-                case SourceSet.MAIN_SOURCE_SET_NAME: return 0
-                case SourceSet.TEST_SOURCE_SET_NAME: return 1
-                default: return 2
-            }
-        }
-    }
-
-    private List<File> sortSourceDirsAsPerUsualConvention(Collection<File> sourceDirs) {
-        return sourceDirs.sort { sourceDir ->
-            if (sourceDir.path.endsWith("java")) { 0 }
-            else if (sourceDir.path.endsWith("resources")) { 2 }
-            else { 1 }
-        }
-    }
-
-    protected Set getAllDeps(Set deps, Set allDeps = []) {
-        deps.each { ResolvedDependency resolvedDependency ->
-            def notSeenBefore = allDeps.add(resolvedDependency)
-            if (notSeenBefore) { // defend against circular dependencies
-                getAllDeps(resolvedDependency.children, allDeps)
-            }
-        }
-        allDeps
-    }
-
-    protected void addSourceArtifact(DefaultExternalModuleDependency dependency) {
-        dependency.artifact { artifact ->
-            artifact.name = dependency.name
-            artifact.type = 'source'
-            artifact.extension = 'jar'
-            artifact.classifier = 'sources'
-        }
-    }
-
-    protected void addJavadocArtifact(DefaultExternalModuleDependency dependency) {
-        dependency.artifact { artifact ->
-            artifact.name = dependency.name
-            artifact.type = 'javadoc'
-            artifact.extension = 'jar'
-            artifact.classifier = 'javadoc'
-        }
-    }
-}
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/internal/PathUtil.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/internal/PathUtil.groovy
deleted file mode 100644
index 6cb16f4..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/internal/PathUtil.groovy
+++ /dev/null
@@ -1,27 +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.plugins.eclipse.model.internal
-
-import org.apache.commons.io.FilenameUtils
-
-/**
- * @author Hans Dockter
- */
-class PathUtil {
-  static String normalizePath(String path) {
-        FilenameUtils.separatorsToUnix(path)
-    }
-}
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/internal/WtpFactory.groovy b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/internal/WtpFactory.groovy
deleted file mode 100644
index 92697ad..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/internal/WtpFactory.groovy
+++ /dev/null
@@ -1,117 +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.plugins.eclipse.model.internal
-
-import org.apache.commons.io.FilenameUtils
-import org.gradle.api.artifacts.Dependency
-import org.gradle.api.artifacts.ExternalDependency
-import org.gradle.api.artifacts.SelfResolvingDependency
-import org.gradle.api.file.SourceDirectorySet
-import org.gradle.api.tasks.SourceSet
-import org.gradle.plugins.eclipse.model.WbDependentModule
-import org.gradle.plugins.eclipse.model.WbProperty
-import org.gradle.plugins.eclipse.model.WbResource
-import org.gradle.plugins.eclipse.model.Wtp
-import org.gradle.plugins.eclipse.EclipseWtp
-
-/**
- * @author Hans Dockter
- */
-class WtpFactory {
-    Wtp createWtp(EclipseWtp eclipseWtp) {
-        File componentInputFile = eclipseWtp.orgEclipseWstCommonComponentInputFile
-        File facetInputFile = eclipseWtp.orgEclipseWstCommonProjectFacetCoreInputFile
-        FileReader componentReader = componentInputFile != null && componentInputFile.exists() ? new FileReader(componentInputFile) : null
-        FileReader facetReader = facetInputFile != null && facetInputFile.exists() ? new FileReader(facetInputFile) : null
-        List entries = getEntriesFromSourceSets(eclipseWtp.sourceSets, eclipseWtp.project)
-        entries.addAll(eclipseWtp.resources)
-        entries.addAll(eclipseWtp.properties)
-        entries.addAll(getEntriesFromConfigurations(eclipseWtp))
-        return new Wtp(eclipseWtp, entries, componentReader, facetReader)
-    }
-
-    List getEntriesFromSourceSets(def sourceSets, def project) {
-        List entries = []
-        sourceSets.each { SourceSet sourceSet ->
-            entries.add(new WbProperty('java-output-path', PathUtil.normalizePath(project.relativePath(sourceSet.classesDir))))
-            sourceSet.allSource.sourceTrees.each { SourceDirectorySet sourceDirectorySet ->
-                sourceDirectorySet.srcDirs.each { dir ->
-                    if (dir.isDirectory()) {
-                        entries.add(new WbResource("/WEB-INF/classes", project.relativePath(dir)))
-                    }
-                }
-            }
-        }
-        entries
-    }
-
-    List getEntriesFromConfigurations(EclipseWtp eclipseWtp) {
-        (getProjectsDependencies(eclipseWtp) as List) + (getLibraries(eclipseWtp) as List)
-    }
-
-    protected Set getProjectsDependencies(EclipseWtp eclipseWtp) {
-        return getDependencies(eclipseWtp.plusConfigurations, eclipseWtp.minusConfigurations, { it instanceof org.gradle.api.artifacts.ProjectDependency }).collect { projectDependency ->
-            projectDependency.dependencyProject
-        }.collect { dependencyProject ->
-            new WbDependentModule("/WEB-INF/lib", "module:/resource/" + dependencyProject.name + "/" + dependencyProject.name)
-        }
-    }
-
-    protected Set getLibraries(EclipseWtp eclipseWtp) {
-        Set declaredDependencies = getDependencies(eclipseWtp.plusConfigurations, eclipseWtp.minusConfigurations,
-                { it instanceof ExternalDependency})
-
-        Set libFiles = eclipseWtp.project.configurations.detachedConfiguration((declaredDependencies as Dependency[])).files +
-                getSelfResolvingFiles(getDependencies(eclipseWtp.plusConfigurations, eclipseWtp.minusConfigurations,
-                        { it instanceof SelfResolvingDependency && !(it instanceof org.gradle.api.artifacts.ProjectDependency)}))
-
-        libFiles.collect { file ->
-            createWbDependentModuleEntry(file, eclipseWtp.variables)
-        }
-    }
-
-    private def getSelfResolvingFiles(Collection dependencies) {
-        dependencies.inject([] as LinkedHashSet) { result, SelfResolvingDependency selfResolvingDependency ->
-            result.addAll(selfResolvingDependency.resolve())
-            result
-        }
-    }
-
-    WbDependentModule createWbDependentModuleEntry(File file, Map<String, File> variables) {
-        def usedVariableEntry = variables.find { name, value -> file.canonicalPath.startsWith(value.canonicalPath) }
-        String handleSnippet;
-        if (usedVariableEntry) {
-            handleSnippet = "var/$usedVariableEntry.key/${file.canonicalPath.substring(usedVariableEntry.value.canonicalPath.length())}"
-        } else {
-            handleSnippet = "lib/${file.canonicalPath}"
-        }
-        handleSnippet = FilenameUtils.separatorsToUnix(handleSnippet)
-        return new WbDependentModule('/WEB-INF/lib', "module:/classpath/$handleSnippet")
-    }
-
-    private Set getDependencies(Set plusConfigurations, Set minusConfigurations, Closure filter) {
-        Set declaredDependencies = new LinkedHashSet()
-        plusConfigurations.each { configuration ->
-            declaredDependencies.addAll(configuration.getAllDependencies().findAll(filter))
-        }
-        minusConfigurations.each { configuration ->
-            configuration.getAllDependencies().findAll(filter).each { minusDep ->
-                declaredDependencies.remove(minusDep)
-            }
-        }
-        return declaredDependencies
-    }
-}
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/package-info.java b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/package-info.java
deleted file mode 100644
index a619e08..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/package-info.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.
- */
-
-/**
- * Classes for the model used by the {@link org.gradle.plugins.eclipse.EclipsePlugin}.
- */
-package org.gradle.plugins.eclipse.model;
diff --git a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/package-info.java b/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/package-info.java
deleted file mode 100644
index d5f39db..0000000
--- a/subprojects/eclipse/src/main/groovy/org/gradle/plugins/eclipse/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * A {link org.gradle.api.Plugin} for generating Eclipse files. 
- */
-package org.gradle.plugins.eclipse;
diff --git a/subprojects/eclipse/src/main/resources/META-INF/gradle-plugins/eclipse.properties b/subprojects/eclipse/src/main/resources/META-INF/gradle-plugins/eclipse.properties
deleted file mode 100644
index 961d42c..0000000
--- a/subprojects/eclipse/src/main/resources/META-INF/gradle-plugins/eclipse.properties
+++ /dev/null
@@ -1,16 +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.
-#
-implementation-class=org.gradle.plugins.eclipse.EclipsePlugin
diff --git a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/EclipseClasspathTest.groovy b/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/EclipseClasspathTest.groovy
deleted file mode 100644
index 592fd16..0000000
--- a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/EclipseClasspathTest.groovy
+++ /dev/null
@@ -1,54 +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.plugins.eclipse;
-
-
-import org.gradle.api.internal.ConventionTask
-import org.gradle.api.tasks.AbstractSpockTaskTest
-
-/**
- * @author Hans Dockter
- */
-public class EclipseClasspathTest extends AbstractSpockTaskTest {
-
-    private EclipseClasspath eclipseClasspath;
-
-    ConventionTask getTask() {
-        return eclipseClasspath
-    }
-
-    def setup() {
-        eclipseClasspath = createTask(EclipseClasspath.class);
-    }
-
-    def containers_shouldAdd() {
-        when:
-        eclipseClasspath.containers "container1"
-        eclipseClasspath.containers "container2"
-
-        then:
-        eclipseClasspath.containers == ['container1', 'container2'] as Set
-    }
-
-    def variables_shouldAdd() {
-        when:
-        eclipseClasspath.variables variable1: 'value1'
-        eclipseClasspath.variables variable2: 'value2'
-
-        then:
-        eclipseClasspath.variables == [variable1: 'value1', variable2: 'value2']
-    }
-}
diff --git a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/EclipsePluginTest.groovy b/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/EclipsePluginTest.groovy
deleted file mode 100644
index 9d0cedd..0000000
--- a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/EclipsePluginTest.groovy
+++ /dev/null
@@ -1,180 +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.plugins.eclipse
-
-import org.gradle.api.Project
-import org.gradle.api.Task
-import org.gradle.api.internal.project.DefaultProject
-import org.gradle.api.tasks.Delete
-import org.gradle.plugins.eclipse.model.BuildCommand
-import org.gradle.plugins.eclipse.model.Facet
-import org.gradle.util.HelperUtil
-import spock.lang.Specification
-import org.gradle.plugins.eclipse.model.WbResource
-
-/**
- * @author Hans Dockter
- */
-class EclipsePluginTest extends Specification {
-    private final DefaultProject project = HelperUtil.createRootProject()
-    private final EclipsePlugin eclipsePlugin = new EclipsePlugin()
-
-    def applyToBaseProject_shouldOnlyHaveEclipseProjectTask() {
-        when:
-        eclipsePlugin.apply(project)
-
-        then:
-        project.tasks.findByPath(':eclipseClasspath') == null
-        assertThatCleanEclipseDependsOn(project, project.cleanEclipseProject)
-        checkEclipseProjectTask([], [])
-    }
-
-    def applyToJavaProject_shouldOnlyHaveProjectAndClasspathTaskForJava() {
-        when:
-        project.apply(plugin: 'java-base')
-        eclipsePlugin.apply(project)
-
-        then:
-        assertThatCleanEclipseDependsOn(project, project.cleanEclipseProject)
-        assertThatCleanEclipseDependsOn(project, project.cleanEclipseClasspath)
-        checkEclipseProjectTask([new BuildCommand('org.eclipse.jdt.core.javabuilder')], ['org.eclipse.jdt.core.javanature'])
-        checkEclipseClasspath([] as Set)
-        checkEclipseJdt()
-
-        when:
-        project.apply(plugin: 'java')
-
-        then:
-        checkEclipseClasspath([project.configurations.testRuntime] as Set)
-    }
-
-    def applyToWarProject_shouldHaveProjectForWebAndClasspathTask() {
-        when:
-        project.apply(plugin: 'war')
-        eclipsePlugin.apply(project)
-
-        then:
-        assertThatCleanEclipseDependsOn(project, project.cleanEclipseProject)
-        assertThatCleanEclipseDependsOn(project, project.cleanEclipseClasspath)
-        assertThatCleanEclipseDependsOn(project, project.cleanEclipseWtp)
-        checkEclipseProjectTask([
-                new BuildCommand('org.eclipse.jdt.core.javabuilder'),
-                new BuildCommand('org.eclipse.wst.common.project.facet.core.builder'),
-                new BuildCommand('org.eclipse.wst.validation.validationbuilder')],
-                ['org.eclipse.jdt.core.javanature',
-                        'org.eclipse.wst.common.project.facet.core.nature',
-                        'org.eclipse.wst.common.modulecore.ModuleCoreNature'])
-        checkEclipseClasspath([project.configurations.testRuntime] as Set)
-        checkEclipseWtp()
-    }
-
-    def applyToScalaProject_shouldHaveProjectAndClasspathTaskForScala() {
-        when:
-        project.apply(plugin: 'scala-base')
-        eclipsePlugin.apply(project)
-
-        then:
-        assertThatCleanEclipseDependsOn(project, project.cleanEclipseProject)
-        assertThatCleanEclipseDependsOn(project, project.cleanEclipseClasspath)
-        checkEclipseProjectTask([new BuildCommand('ch.epfl.lamp.sdt.core.scalabuilder')],
-                ['ch.epfl.lamp.sdt.core.scalanature', 'org.eclipse.jdt.core.javanature'])
-        checkEclipseClasspath([] as Set)
-
-        when:
-        project.apply(plugin: 'scala')
-
-        then:
-        checkEclipseClasspath([project.configurations.testRuntime] as Set)
-    }
-
-    def applyToGroovyProject_shouldHaveProjectAndClasspathTaskForGroovy() {
-        when:
-        project.apply(plugin: 'groovy-base')
-        eclipsePlugin.apply(project)
-
-        then:
-        assertThatCleanEclipseDependsOn(project, project.cleanEclipseProject)
-        assertThatCleanEclipseDependsOn(project, project.cleanEclipseClasspath)
-        checkEclipseProjectTask([new BuildCommand('org.eclipse.jdt.core.javabuilder')], ['org.eclipse.jdt.groovy.core.groovyNature',
-                'org.eclipse.jdt.core.javanature'])
-        checkEclipseClasspath([] as Set)
-
-        when:
-        project.apply(plugin: 'groovy')
-
-        then:
-        checkEclipseClasspath([project.configurations.testRuntime] as Set)
-    }
-
-    private void checkEclipseProjectTask(List buildCommands, List natures) {
-        EclipseProject eclipseProjectTask = project.eclipseProject
-        assert eclipseProjectTask instanceof EclipseProject
-        assert project.eclipse.taskDependencies.getDependencies(project.eclipse).contains(eclipseProjectTask)
-        assert eclipseProjectTask.buildCommands == buildCommands
-        assert eclipseProjectTask.natures == natures
-        assert eclipseProjectTask.links == [] as Set
-        assert eclipseProjectTask.referencedProjects == [] as Set
-        assert eclipseProjectTask.comment == null
-        assert eclipseProjectTask.projectName == project.name
-        assert eclipseProjectTask.outputFile == project.file('.project')
-    }
-
-    private void checkEclipseClasspath(def configurations) {
-        EclipseClasspath eclipseClasspath = project.eclipseClasspath
-        assert eclipseClasspath instanceof EclipseClasspath
-        assert project.eclipse.taskDependencies.getDependencies(project.eclipse).contains(eclipseClasspath)
-        assert eclipseClasspath.sourceSets == project.sourceSets
-        assert eclipseClasspath.plusConfigurations == configurations
-        assert eclipseClasspath.minusConfigurations == [] as Set
-        assert eclipseClasspath.containers == ['org.eclipse.jdt.launching.JRE_CONTAINER'] as Set
-        assert eclipseClasspath.outputFile == project.file('.classpath')
-        assert eclipseClasspath.defaultOutputDir == new File(project.projectDir, 'bin')
-    }
-
-    private void checkEclipseJdt() {
-        EclipseJdt eclipseJdt = project.eclipseJdt
-        assert project.eclipse.taskDependencies.getDependencies(project.eclipse).contains(eclipseJdt)
-        assert eclipseJdt.sourceCompatibility == project.sourceCompatibility
-        assert eclipseJdt.targetCompatibility == project.targetCompatibility
-        assert eclipseJdt.outputFile == project.file('.settings/org.eclipse.jdt.core.prefs')
-    }
-
-    private void checkEclipseWtp() {
-        EclipseWtp eclipseWtp = project.eclipseWtp
-        assert eclipseWtp instanceof EclipseWtp
-        assert project.eclipse.taskDependencies.getDependencies(project.eclipse).contains(eclipseWtp)
-        assert eclipseWtp.sourceSets.all == [project.sourceSets.main] as Set
-        assert eclipseWtp.plusConfigurations == [project.configurations.runtime] as Set
-        assert eclipseWtp.minusConfigurations == [project.configurations.providedRuntime] as Set
-        assert eclipseWtp.deployName == project.name
-        assert eclipseWtp.contextPath == project.war.baseName
-        assert eclipseWtp.orgEclipseWstCommonComponentInputFile == project.file('.settings/org.eclipse.wst.common.component')
-        assert eclipseWtp.orgEclipseWstCommonComponentOutputFile == project.file('.settings/org.eclipse.wst.common.component')
-        assert eclipseWtp.orgEclipseWstCommonProjectFacetCoreInputFile == project.file('.settings/org.eclipse.wst.common.project.facet.core.xml')
-        assert eclipseWtp.orgEclipseWstCommonProjectFacetCoreOutputFile == project.file('.settings/org.eclipse.wst.common.project.facet.core.xml')
-        assert eclipseWtp.facets == [new Facet("jst.web", "2.4"), new Facet("jst.java", "5.0")]
-        assert eclipseWtp.variables == [:]
-        assert eclipseWtp.resources == [new WbResource('/', project.convention.plugins.war.webAppDirName)]
-    }
-
-    void assertThatCleanEclipseDependsOn(Project project, Task dependsOnTask) {
-        assert dependsOnTask instanceof Delete
-        assert project.cleanEclipse.taskDependencies.getDependencies(project.cleanEclipse).contains(dependsOnTask)
-    }
-}
-
diff --git a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/EclipseProjectTest.groovy b/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/EclipseProjectTest.groovy
deleted file mode 100644
index 3f493cb..0000000
--- a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/EclipseProjectTest.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.plugins.eclipse
-
-import org.gradle.api.internal.ConventionTask
-import org.gradle.api.tasks.AbstractSpockTaskTest
-import org.gradle.plugins.eclipse.model.BuildCommand
-
-/**
- * @author Hans Dockter
- */
-class EclipseProjectTest extends AbstractSpockTaskTest {
-    EclipseProject eclipseProject
-
-    ConventionTask getTask() {
-        return eclipseProject
-    }
-
-    def setup() {
-        eclipseProject = createTask(EclipseProject.class);
-    }
-
-    def natures_shouldAdd() {
-        when:
-        eclipseProject.natures 'nature1'
-        eclipseProject.natures 'nature2'
-
-        then:
-        eclipseProject.natures == ['nature1', 'nature2']
-    }
-
-    def buildCommands_shouldAdd() {
-        when:
-        eclipseProject.buildCommand 'command1', key1: 'value1'
-        eclipseProject.buildCommand 'command2'
-
-        then:
-        eclipseProject.buildCommands as List == [new BuildCommand('command1', [key1: 'value1']), new BuildCommand('command2')]
-    }
-}
diff --git a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/EclipseWtpTest.groovy b/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/EclipseWtpTest.groovy
deleted file mode 100644
index ee80d58..0000000
--- a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/EclipseWtpTest.groovy
+++ /dev/null
@@ -1,96 +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.plugins.eclipse;
-
-
-import org.gradle.api.internal.ConventionTask
-import org.gradle.api.tasks.AbstractSpockTaskTest
-import org.gradle.plugins.eclipse.model.Facet
-import org.gradle.plugins.eclipse.model.WbProperty
-import org.gradle.plugins.eclipse.model.WbResource
-import org.gradle.plugins.eclipse.model.Wtp
-import org.gradle.util.TemporaryFolder
-import org.junit.Rule
-import org.gradle.plugins.eclipse.model.internal.WtpFactory
-
-/**
- * @author Hans Dockter
- */
-public class EclipseWtpTest extends AbstractSpockTaskTest {
-    EclipseWtp eclipseWtp
-
-    @Rule
-    TemporaryFolder tmpDir = new TemporaryFolder()
-
-    ConventionTask getTask() {
-        return eclipseWtp
-    }
-
-    def setup() {
-        eclipseWtp = createTask(EclipseWtp.class);
-    }
-
-    def facet_shouldAdd() {
-        when:
-        eclipseWtp.facet name: 'facet1', version: '1.0'
-        eclipseWtp.facet name: 'facet2', version: '2.0'
-
-        then:
-        eclipseWtp.facets == [new Facet('facet1', '1.0'), new Facet('facet2', '2.0')]
-    }
-
-    def property_shouldAdd() {
-        when:
-        eclipseWtp.property name: 'prop1', value: 'value1'
-        eclipseWtp.property name: 'prop2', value: 'value2'
-
-        then:
-        eclipseWtp.properties == [new WbProperty('prop1', 'value1'), new WbProperty('prop2', 'value2')]
-    }
-
-    def resource_shouldAdd() {
-        when:
-        eclipseWtp.resource deployPath: 'path1', sourcePath: 'sourcepath1'
-        eclipseWtp.resource deployPath: 'path2', sourcePath: 'sourcepath2'
-
-        then:
-        eclipseWtp.resources == [new WbResource('path1', 'sourcepath1'), new WbResource('path2', 'sourcepath2')]
-    }
-
-    def generateXml() {
-        WtpFactory modelFactory = Mock()
-        Wtp wtp = Mock()
-        eclipseWtp.modelFactory = modelFactory
-        eclipseWtp.setOrgEclipseWstCommonComponentOutputFile(tmpDir.file("component"))
-        eclipseWtp.setOrgEclipseWstCommonProjectFacetCoreOutputFile(tmpDir.file("facet"))
-        modelFactory.createWtp(eclipseWtp) >> wtp
-
-        when:
-        eclipseWtp.generateXml()
-
-        then:
-        1 * wtp.toXml(eclipseWtp.orgEclipseWstCommonComponentOutputFile, eclipseWtp.orgEclipseWstCommonProjectFacetCoreOutputFile)
-    }
-
-    def variables_shouldAdd() {
-        when:
-        eclipseWtp.variables variable1: 'value1'
-        eclipseWtp.variables variable2: 'value2'
-
-        then:
-        eclipseWtp.variables == [variable1: 'value1', variable2: 'value2']
-    }
-}
diff --git a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/ClasspathTest.groovy b/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/ClasspathTest.groovy
deleted file mode 100644
index edc7cd0..0000000
--- a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/ClasspathTest.groovy
+++ /dev/null
@@ -1,100 +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.plugins.eclipse.model;
-
-import org.gradle.api.internal.XmlTransformer
-import org.gradle.util.TemporaryFolder
-import org.junit.Rule
-import spock.lang.Specification
-
-/**
- * @author Hans Dockter
- */
-public class ClasspathTest extends Specification {
-    private static final CUSTOM_ENTRIES = [
-            new ProjectDependency("/test2", false, null, [] as Set),
-            new Container("org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6",
-                false, null, [] as Set),
-            new Library("/apache-ant-1.7.1/lib/ant-antlr.jar", false, null, [] as Set, null, null),
-            new SourceFolder("src", null, [] as Set, "bin2", [], []),
-            new Variable("GRADLE_CACHE/ant-1.6.5.jar", false, null, [] as Set, null, null),
-            new Container("org.eclipse.jdt.USER_LIBRARY/gradle", false, null, [] as Set),
-            new Output("bin")]
-    private static final PROJECT_DEPENDENCY = [CUSTOM_ENTRIES[0]]
-    private static final ALL_DEPENDENCIES = [CUSTOM_ENTRIES[0], CUSTOM_ENTRIES[2]]
-
-    final Classpath classpath = new Classpath(new XmlTransformer())
-
-    @Rule
-    public TemporaryFolder tmpDir = new TemporaryFolder();
-
-    def loadFromReader() {
-        when:
-        classpath.load(customClasspathReader)
-
-        then:
-        classpath.entries == CUSTOM_ENTRIES
-    }
-
-    def configureOverwritesDependenciesAndAppendsAllOtherEntries() {
-        def constructorEntries = [createSomeLibrary()]
-
-        when:
-        classpath.load(customClasspathReader)
-        def newEntries = constructorEntries + PROJECT_DEPENDENCY
-        classpath.configure(newEntries)
-
-        then:
-        def entriesToBeKept = CUSTOM_ENTRIES - ALL_DEPENDENCIES
-        classpath.entries ==  entriesToBeKept + newEntries
-    }
-
-    def loadDefaults() {
-        when:
-        classpath.loadDefaults()
-
-        then:
-        classpath.entries == []
-    }
-
-    def toXml_shouldContainCustomValues() {
-        def constructorEntries = [createSomeLibrary()]
-
-        when:
-        classpath.load(customClasspathReader)
-        classpath.configure(constructorEntries)
-        def xml = getToXmlReader()
-        def other = new Classpath(new XmlTransformer())
-        other.load(xml)
-
-        then:
-        classpath == other
-    }
-
-    private InputStream getCustomClasspathReader() {
-        return getClass().getResourceAsStream('customClasspath.xml')
-    }
-
-    private Library createSomeLibrary() {
-        return new Library("/somepath", true, null, [] as Set, null, null)
-    }
-
-    private InputStream getToXmlReader() {
-        ByteArrayOutputStream toXmlText = new ByteArrayOutputStream()
-        classpath.store(toXmlText)
-        return new ByteArrayInputStream(toXmlText.toByteArray())
-    }
-}
\ No newline at end of file
diff --git a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/ContainerTest.groovy b/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/ContainerTest.groovy
deleted file mode 100644
index 21a544a..0000000
--- a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/ContainerTest.groovy
+++ /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.plugins.eclipse.model
-
-import spock.lang.Specification
-
-/**
- * @author Hans Dockter
- */
-
-class ContainerTest extends Specification {
-    final static String XML_TEXT = '''
-                <classpathentry exported="true" kind="con" path="somePath">
-                    <attributes>
-                        <attribute name="org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY" value="mynative"/>
-                    </attributes>
-                    <accessrules>
-                        <accessrule kind="nonaccessible" pattern="secret**"/>
-                    </accessrules>
-                </classpathentry>'''
-
-    def canReadFromXml() {
-        when:
-        Container container = new Container(new XmlParser().parseText(XML_TEXT))
-
-        then:
-        container == createContainer()
-    }
-
-    def canWriteToXml() {
-        Node rootNode = new Node(null, 'root')
-
-        when:
-        createContainer().appendNode(rootNode)
-
-        then:
-        new Container(rootNode.classpathentry[0]) == createContainer()
-    }
-
-    def equality() {
-        Container container = createContainer()
-        container.nativeLibraryLocation += 'x'
-
-        expect:
-        container != createContainer()
-    }
-
-    private Container createContainer() {
-        return new Container('somePath', true, 'mynative', [new AccessRule('nonaccessible', 'secret**')] as Set)
-    }
-
-
-}
\ No newline at end of file
diff --git a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/FacetTest.groovy b/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/FacetTest.groovy
deleted file mode 100644
index 6afc7d7..0000000
--- a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/FacetTest.groovy
+++ /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.plugins.eclipse.model
-
-import spock.lang.Specification
-
-/**
- * @author Hans Dockter
- */
-
-class FacetTest extends Specification {
-    final static String XML_TEXT = '<installed facet="jst.web" version="2.4"/>'
-
-    def canReadFromXml() {
-        when:
-        Facet facet = new Facet(new XmlParser().parseText(XML_TEXT))
-
-        then:
-        facet == createFacet()
-    }
-
-    def canWriteToXml() {
-        Node rootNode = new Node(null, 'root')
-
-        when:
-        createFacet().appendNode(rootNode)
-
-        then:
-        new Facet(rootNode.installed[0]) == createFacet()
-    }
-
-    def equality() {
-        Facet facet = createFacet()
-        facet.name += 'x'
-
-        expect:
-        facet != createFacet()
-    }
-
-    private Facet createFacet() {
-        return new Facet("jst.web", "2.4")
-    }
-
-
-}
\ No newline at end of file
diff --git a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/JdtTest.groovy b/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/JdtTest.groovy
deleted file mode 100644
index 201254d..0000000
--- a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/JdtTest.groovy
+++ /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.plugins.eclipse.model
-
-import spock.lang.Specification
-import org.gradle.api.JavaVersion
-
-class JdtTest extends Specification {
-    final Jdt jdt = new Jdt()
-
-    def defaultsForJava1_3Source() {
-        Properties properties = new Properties()
-
-        when:
-        jdt.loadDefaults()
-        jdt.sourceCompatibility = JavaVersion.VERSION_1_3
-        jdt.targetCompatibility = JavaVersion.VERSION_1_3
-        store(properties)
-
-        then:
-        properties['org.eclipse.jdt.core.compiler.compliance'] == '1.3'
-        properties['org.eclipse.jdt.core.compiler.source'] == '1.3'
-        properties['org.eclipse.jdt.core.compiler.problem.assertIdentifier'] == 'ignore'
-        properties['org.eclipse.jdt.core.compiler.problem.enumIdentifier'] == 'ignore'
-        properties['org.eclipse.jdt.core.compiler.codegen.targetPlatform'] == '1.3'
-    }
-
-    def defaultsForJava1_4Source() {
-        Properties properties = new Properties()
-
-        when:
-        jdt.loadDefaults()
-        jdt.sourceCompatibility = JavaVersion.VERSION_1_4
-        jdt.targetCompatibility = JavaVersion.VERSION_1_4
-        store(properties)
-
-        then:
-        properties['org.eclipse.jdt.core.compiler.compliance'] == '1.4'
-        properties['org.eclipse.jdt.core.compiler.source'] == '1.4'
-        properties['org.eclipse.jdt.core.compiler.problem.assertIdentifier'] == 'error'
-        properties['org.eclipse.jdt.core.compiler.problem.enumIdentifier'] == 'warning'
-        properties['org.eclipse.jdt.core.compiler.codegen.targetPlatform'] == '1.4'
-    }
-
-    def defaultsForJava1_5Source() {
-        Properties properties = new Properties()
-
-        when:
-        jdt.loadDefaults()
-        jdt.sourceCompatibility = JavaVersion.VERSION_1_5
-        jdt.targetCompatibility = JavaVersion.VERSION_1_5
-        store(properties)
-
-        then:
-        properties['org.eclipse.jdt.core.compiler.compliance'] == '1.5'
-        properties['org.eclipse.jdt.core.compiler.source'] == '1.5'
-        properties['org.eclipse.jdt.core.compiler.problem.assertIdentifier'] == 'error'
-        properties['org.eclipse.jdt.core.compiler.problem.enumIdentifier'] == 'error'
-        properties['org.eclipse.jdt.core.compiler.codegen.targetPlatform'] == '1.5'
-    }
-
-    def defaultsForJava1_6Source() {
-        Properties properties = new Properties()
-
-        when:
-        jdt.loadDefaults()
-        jdt.sourceCompatibility = JavaVersion.VERSION_1_6
-        jdt.targetCompatibility = JavaVersion.VERSION_1_6
-        store(properties)
-
-        then:
-        properties['org.eclipse.jdt.core.compiler.compliance'] == '1.6'
-        properties['org.eclipse.jdt.core.compiler.source'] == '1.6'
-        properties['org.eclipse.jdt.core.compiler.problem.assertIdentifier'] == 'error'
-        properties['org.eclipse.jdt.core.compiler.problem.enumIdentifier'] == 'error'
-        properties['org.eclipse.jdt.core.compiler.codegen.targetPlatform'] == '1.6'
-    }
-
-    def store(Properties properties) {
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream()
-        jdt.store(outputStream)
-        properties.load(new ByteArrayInputStream(outputStream.toByteArray()))
-    }
-}
diff --git a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/LibraryTest.groovy b/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/LibraryTest.groovy
deleted file mode 100644
index af50a78..0000000
--- a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/LibraryTest.groovy
+++ /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.plugins.eclipse.model
-
-import spock.lang.Specification
-
-/**
- * @author Hans Dockter
- */
-
-class LibraryTest extends Specification {
-    final static String XML_TEXT = '''
-                    <classpathentry exported="true" kind="lib" path="/ant.jar" sourcepath="/ant-src.jar">
-                        <attributes>
-                            <attribute name="org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY" value="mynative"/>
-                            <attribute name="javadoc_location" value="jar:file:/ant-javadoc.jar!/path"/>
-                        </attributes>
-                        <accessrules>
-                            <accessrule kind="nonaccessible" pattern="secret**"/>
-                        </accessrules>
-                    </classpathentry>'''
-
-    def canReadFromXml() {
-        when:
-        Library library = new Library(new XmlParser().parseText(XML_TEXT))
-
-        then:
-        library == createLibrary()
-    }
-
-    def canWriteToXml() {
-        Node rootNode = new Node(null, 'root')
-
-        when:
-        createLibrary().appendNode(rootNode)
-
-        then:
-        new Library(rootNode.classpathentry[0]) == createLibrary()
-    }
-
-    def equality() {
-        Library library = createLibrary()
-        library.javadocPath += 'x'
-
-        expect:
-        library != createLibrary()
-    }
-
-    private Library createLibrary() {
-        return new Library('/ant.jar', true, 'mynative', [new AccessRule('nonaccessible', 'secret**')] as Set,
-                "/ant-src.jar", "jar:file:/ant-javadoc.jar!/path")
-    }
-}
\ No newline at end of file
diff --git a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/OutputTest.groovy b/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/OutputTest.groovy
deleted file mode 100644
index 756ddb2..0000000
--- a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/OutputTest.groovy
+++ /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.plugins.eclipse.model
-
-import spock.lang.Specification
-
-/**
- * @author Hans Dockter
- */
-
-class OutputTest extends Specification {
-    final static String XML_TEXT = '<classpathentry kind="output" path="somePath"/>'
-
-    def canReadFromXml() {
-        when:
-        Output output = new Output(new XmlParser().parseText(XML_TEXT))
-
-        then:
-        output == createOutput()
-    }
-
-    def canWriteToXml() {
-        Node rootNode = new Node(null, 'root')
-
-        when:
-        createOutput().appendNode(rootNode)
-
-        then:
-        new Output(rootNode.classpathentry[0]) == createOutput()
-    }
-
-    def equality() {
-        Output output = createOutput()
-        output.path += 'x'
-
-        expect:
-        output != createOutput()
-    }
-
-    private Output createOutput() {
-        return new Output('somePath')
-    }
-
-
-}
\ No newline at end of file
diff --git a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/ProjectDependencyTest.groovy b/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/ProjectDependencyTest.groovy
deleted file mode 100644
index 5b270ee..0000000
--- a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/ProjectDependencyTest.groovy
+++ /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.plugins.eclipse.model
-
-import spock.lang.Specification
-
-/**
- * @author Hans Dockter
- */
-
-class ProjectDependencyTest extends Specification {
-    final static String XML_TEXT = '''
-                <classpathentry kind="src" path="/test2" exported="true">
-                    <attributes>
-                        <attribute name="org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY" value="mynative"/>
-                    </attributes>
-                    <accessrules>
-                        <accessrule kind="nonaccessible" pattern="secret**"/>
-                    </accessrules>
-                </classpathentry>'''
-
-    def canReadFromXml() {
-        when:
-        ProjectDependency projectDependency = new ProjectDependency(new XmlParser().parseText(XML_TEXT))
-
-        then:
-        projectDependency == createProjectDependency()
-    }
-
-    def canWriteToXml() {
-        Node rootNode = new Node(null, 'root')
-
-        when:
-        createProjectDependency().appendNode(rootNode)
-
-        then:
-        new ProjectDependency(rootNode.classpathentry[0]) == createProjectDependency()
-    }
-
-    def equality() {
-        ProjectDependency projectDependency = createProjectDependency()
-        projectDependency.nativeLibraryLocation += 'x'
-
-        expect:
-        projectDependency != createProjectDependency()
-    }
-
-    private ProjectDependency createProjectDependency() {
-        return new ProjectDependency('/test2', true, 'mynative', [new AccessRule('nonaccessible', 'secret**')] as Set)
-    }
-
-
-}
\ No newline at end of file
diff --git a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/ProjectTest.groovy b/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/ProjectTest.groovy
deleted file mode 100644
index 08b95f4..0000000
--- a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/ProjectTest.groovy
+++ /dev/null
@@ -1,113 +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.plugins.eclipse.model;
-
-
-import org.gradle.api.internal.XmlTransformer
-import org.gradle.plugins.eclipse.EclipseProject
-import org.gradle.util.HelperUtil
-import org.gradle.util.TemporaryFolder
-import org.junit.Rule
-import spock.lang.Specification
-
-/**
- * @author Hans Dockter
- */
-public class ProjectTest extends Specification {
-    def static final CUSTOM_REFERENCED_PROJECTS = ['refProject'] as LinkedHashSet
-    def static final CUSTOM_BUILD_COMMANDS = [new BuildCommand('org.eclipse.jdt.core.scalabuilder', [climate: 'cold'])]
-    def static final CUSTOM_NATURES = ['org.eclipse.jdt.core.scalanature'] 
-    def static final CUSTOM_LINKS = [new Link('somename', 'sometype', 'somelocation', '')] as Set
-
-    @Rule
-    public TemporaryFolder tmpDir = new TemporaryFolder();
-    final Project project = new Project(new XmlTransformer())
-
-    def loadFromReader() {
-        when:
-        project.load(customProjectReader)
-
-        then:
-        project.name == 'test'
-        project.comment == 'for testing'
-        project.referencedProjects == CUSTOM_REFERENCED_PROJECTS
-        project.buildCommands == CUSTOM_BUILD_COMMANDS
-        project.natures == CUSTOM_NATURES
-        project.links == CUSTOM_LINKS
-    }
-
-    def configureMergesValues() {
-        EclipseProject task = HelperUtil.createTask(EclipseProject)
-        task.projectName = 'constructorName'
-        task.comment = 'constructorComment'
-        task.referencedProjects = ['constructorRefProject'] as LinkedHashSet
-        task.buildCommands = [new BuildCommand('constructorbuilder')]
-        task.natures = ['constructorNature']
-        task.links = [new Link('constructorName', 'constructorType', 'constructorLocation', '')] as Set
-
-        when:
-        project.load(customProjectReader)
-        project.configure(task)
-
-        then:
-        project.name == task.projectName
-        project.comment == task.comment
-        project.referencedProjects == task.referencedProjects + CUSTOM_REFERENCED_PROJECTS
-        project.buildCommands == CUSTOM_BUILD_COMMANDS + task.buildCommands
-        project.natures == CUSTOM_NATURES + task.natures
-        project.links == task.links + CUSTOM_LINKS
-    }
-
-    def loadDefaults() {
-        when:
-        project.loadDefaults()
-
-        then:
-        project.name == ""
-        project.comment == ""
-        project.referencedProjects == [] as Set
-        project.buildCommands == []
-        project.natures == []
-        project.links == [] as Set
-    }
-
-    def toXml_shouldContainCustomValues() {
-        EclipseProject task = HelperUtil.createTask(EclipseProject)
-        task.projectName = 'constructorName'
-        task.comment = 'constructorComment'
-        task.referencedProjects = ['constructorRefProject'] as LinkedHashSet
-
-        when:
-        project.load(customProjectReader)
-        project.configure(task)
-        def xml = getToXmlReader()
-        def other = new Project(new XmlTransformer())
-        other.load(xml)
-
-        then:
-        project == other
-    }
-
-    private InputStream getCustomProjectReader() {
-        return getClass().getResourceAsStream('customProject.xml')
-    }
-
-    private InputStream getToXmlReader() {
-        ByteArrayOutputStream toXmlText = new ByteArrayOutputStream()
-        project.store(toXmlText)
-        return new ByteArrayInputStream(toXmlText.toByteArray())
-    }
-}
diff --git a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/SourceFolderTest.groovy b/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/SourceFolderTest.groovy
deleted file mode 100644
index cda61e1..0000000
--- a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/SourceFolderTest.groovy
+++ /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.plugins.eclipse.model
-
-import spock.lang.Specification
-
-/**
- * @author Hans Dockter
- */
-class SourceFolderTest extends Specification {
-    final static String XML_TEXT = '''
-                <classpathentry including="**/Test1*|**/Test2*" excluding="**/Test3*|**/Test4*" kind="src" output="bin2" path="src">
-                    <attributes>
-                        <attribute name="org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY" value="mynative"/>
-                    </attributes>
-                    <accessrules>
-                        <accessrule kind="nonaccessible" pattern="secret**"/>
-                    </accessrules>
-                </classpathentry>'''
-
-    def canReadFromXml() {
-        expect:
-        new SourceFolder(new XmlParser().parseText(XML_TEXT)) == createSourceFolder()
-    }
-
-    def canWriteToXml() {
-        Node rootNode = new Node(null, 'root')
-
-        when:
-        createSourceFolder().appendNode(rootNode)
-
-        then:
-        new SourceFolder(rootNode.classpathentry[0]) == createSourceFolder()
-    }
-
-    def equality() {
-        SourceFolder sourceFolder = createSourceFolder()
-        sourceFolder.nativeLibraryLocation += 'x'
-
-        expect:
-        sourceFolder != createSourceFolder()
-    }
-
-    def createSourceFolder() {
-        return new SourceFolder('src', 'mynative', [new AccessRule('nonaccessible', 'secret**')] as Set,
-            'bin2', ['**/Test1*' ,'**/Test2*'], ['**/Test3*' ,'**/Test4*'])
-    }
-}
diff --git a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/VariableTest.groovy b/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/VariableTest.groovy
deleted file mode 100644
index b1d7fe7..0000000
--- a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/VariableTest.groovy
+++ /dev/null
@@ -1,68 +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.plugins.eclipse.model
-
-import spock.lang.Specification
-
-/**
- * @author Hans Dockter
- */
-
-class VariableTest extends Specification {
-    final static String XML_TEXT = '''
-                <classpathentry exported="true" kind="var" path="/GRADLE_CACHE/ant.jar" sourcepath="/GRADLE_CACHE/ant-src.jar">
-                    <attributes>
-                        <attribute name="org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY" value="mynative"/>
-                        <attribute name="javadoc_location" value="jar:file:/GRADLE_CACHE/ant-javadoc.jar!/path"/>
-                    </attributes>
-                    <accessrules>
-                        <accessrule kind="nonaccessible" pattern="secret**"/>
-                    </accessrules>
-                </classpathentry>'''
-
-    def canReadFromXml() {
-        when:
-        Variable variable = new Variable(new XmlParser().parseText(XML_TEXT))
-
-        then:
-        variable == createVariable()
-    }
-
-    def canWriteToXml() {
-        Node rootNode = new Node(null, 'root')
-
-        when:
-        createVariable().appendNode(rootNode)
-
-        then:
-        new Variable(rootNode.classpathentry[0]) == createVariable()
-    }
-
-    def equality() {
-        Variable variable = createVariable()
-        variable.sourcePath += 'x'
-
-        expect:
-        variable != createVariable()
-    }
-
-    private Variable createVariable() {
-        return new Variable('/GRADLE_CACHE/ant.jar', true, 'mynative', [new AccessRule('nonaccessible', 'secret**')] as Set,
-                "/GRADLE_CACHE/ant-src.jar", "jar:file:/GRADLE_CACHE/ant-javadoc.jar!/path")
-    }
-
-
-}
\ No newline at end of file
diff --git a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/WbDependentModuleTest.groovy b/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/WbDependentModuleTest.groovy
deleted file mode 100644
index dc81592..0000000
--- a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/WbDependentModuleTest.groovy
+++ /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.plugins.eclipse.model
-
-import spock.lang.Specification
-
-/**
- * @author Hans Dockter
- */
-
-class WbDependentModuleTest extends Specification {
-    final static String XML_TEXT = '''
-                <dependent-module deploy-path="/WEB-INF/lib" handle="module:/classpath/gradle-1.0.0.jar">
-                    <dependency-type>uses</dependency-type>
-                </dependent-module>'''
-
-    def canReadFromXml() {
-        when:
-        WbDependentModule wbDependentModule = new WbDependentModule(new XmlParser().parseText(XML_TEXT))
-
-        then:
-        wbDependentModule == createWbDependentModule()
-    }
-
-    def canWriteToXml() {
-        Node rootNode = new Node(null, 'root')
-
-        when:
-        createWbDependentModule().appendNode(rootNode)
-
-        then:
-        new WbDependentModule(rootNode.'dependent-module'[0]) == createWbDependentModule()
-    }
-
-    def equality() {
-        WbDependentModule wbDependentModule = createWbDependentModule()
-        wbDependentModule.handle += 'x'
-
-        expect:
-        wbDependentModule != createWbDependentModule()
-    }
-
-    private WbDependentModule createWbDependentModule() {
-        return new WbDependentModule("/WEB-INF/lib", "module:/classpath/gradle-1.0.0.jar")
-    }
-
-
-}
\ No newline at end of file
diff --git a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/WbPropertyTest.groovy b/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/WbPropertyTest.groovy
deleted file mode 100644
index 1f38af0..0000000
--- a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/WbPropertyTest.groovy
+++ /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.plugins.eclipse.model
-
-import spock.lang.Specification
-
-/**
- * @author Hans Dockter
- */
-
-class WbPropertyTest extends Specification {
-    final static String XML_TEXT = '<property name="java-output-path" value="/build/classes"/>'
-
-    def canReadFromXml() {
-        when:
-        WbProperty wbProperty = new WbProperty(new XmlParser().parseText(XML_TEXT))
-
-        then:
-        wbProperty == createWbProperty()
-    }
-
-    def canWriteToXml() {
-        Node rootNode = new Node(null, 'root')
-
-        when:
-        createWbProperty().appendNode(rootNode)
-
-        then:
-        new WbProperty(rootNode.property[0]) == createWbProperty()
-    }
-
-    def equality() {
-        WbProperty wbProperty = createWbProperty()
-        wbProperty.name += 'x'
-
-        expect:
-        wbProperty != createWbProperty()
-    }
-
-    private WbProperty createWbProperty() {
-        return new WbProperty("java-output-path", "/build/classes")
-    }
-
-
-}
\ No newline at end of file
diff --git a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/WbResourceTest.groovy b/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/WbResourceTest.groovy
deleted file mode 100644
index c7075f9..0000000
--- a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/WbResourceTest.groovy
+++ /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.plugins.eclipse.model
-
-import spock.lang.Specification
-
-/**
- * @author Hans Dockter
- */
-
-class WbResourceTest extends Specification {
-    final static String XML_TEXT = '<wb-resource deploy-path="/" source-path="src/main/webapp"/>'
-
-    def canReadFromXml() {
-        when:
-        WbResource wbResource = new WbResource(new XmlParser().parseText(XML_TEXT))
-
-        then:
-        wbResource == createWbResource()
-    }
-
-    def canWriteToXml() {
-        Node rootNode = new Node(null, 'root')
-
-        when:
-        createWbResource().appendNode(rootNode)
-
-        then:
-        new WbResource(rootNode.'wb-resource'[0]) == createWbResource()
-    }
-
-    def equality() {
-        WbResource wbResource = createWbResource()
-        wbResource.sourcePath += 'x'
-
-        expect:
-        wbResource != createWbResource()
-    }
-
-    private WbResource createWbResource() {
-        return new WbResource("/", "src/main/webapp")
-    }
-
-
-}
\ No newline at end of file
diff --git a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/WtpTest.groovy b/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/WtpTest.groovy
deleted file mode 100644
index 490db2c..0000000
--- a/subprojects/eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/WtpTest.groovy
+++ /dev/null
@@ -1,220 +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.plugins.eclipse.model;
-
-
-import org.gradle.listener.ActionBroadcast
-import org.gradle.plugins.eclipse.EclipseWtp
-import org.gradle.util.TemporaryFolder
-import org.gradle.util.TestFile
-import org.junit.Rule
-import spock.lang.Specification
-
-/**
- * @author Hans Dockter
- */
-
-public class WtpTest extends Specification {
-    private static final List CUSTOM_WB_MODULE_ENTRIES = [
-            new WbDependentModule('/WEB-INF/lib', "module:/classpath/myapp-1.0.0.jar"),
-            new WbResource("/WEB-INF/classes", "src/main/java")]
-    private static final List CUSTOM_FACETS = [new Facet('jst.web', '2.4'), new Facet('jst.java', '1.4')]
-
-    @Rule
-    public TemporaryFolder tmpDir = new TemporaryFolder();
-
-    def initWithReader() {
-        Wtp wtp = createWtp(componentReader: customComponentReader, facetReader: customFacetReader)
-
-        expect:
-        wtp.deployName == 'recu'
-        wtp.contextPath == 'recu'
-        wtp.wbModuleEntries == CUSTOM_WB_MODULE_ENTRIES
-        wtp.facets == CUSTOM_FACETS
-    }
-
-    def initWithReaderAndValues_shouldBeMerged() {
-        def constructorDeployName = 'build'
-        def constructorContextPath = 'context'
-        def constructorWbModuleEntries = [createSomeWbModuleEntry()]
-        def constructorFacets = [createSomeFacet()]
-
-        Wtp wtp = createWtp(wbModuleEntries: constructorWbModuleEntries + [CUSTOM_WB_MODULE_ENTRIES[0]], facets: constructorFacets + [CUSTOM_FACETS[0]],
-                deployName: constructorDeployName, contextPath: constructorContextPath, componentReader: customComponentReader, facetReader: customFacetReader)
-
-        expect:
-        wtp.wbModuleEntries == CUSTOM_WB_MODULE_ENTRIES + constructorWbModuleEntries
-        wtp.deployName == constructorDeployName
-        wtp.contextPath == constructorContextPath
-        wtp.facets == CUSTOM_FACETS + constructorFacets
-    }
-
-    def initWithNullReader() {
-        def constructorDeployName = 'build'
-        def constructorWbModuleEntries = [createSomeWbModuleEntry()]
-        def constructorFacets = [createSomeFacet()]
-
-        Wtp wtp = createWtp(wbModuleEntries: constructorWbModuleEntries, facets: constructorFacets,
-                deployName: constructorDeployName)
-
-        expect:
-        wtp.orgEclipseWstCommonComponentXml != null
-        wtp.orgEclipseWstCommonProjectFacetCoreXml != null
-        wtp.wbModuleEntries == constructorWbModuleEntries
-        wtp.deployName == constructorDeployName
-        wtp.facets == constructorFacets
-    }
-
-    def toXml() {
-        when:
-        Wtp wtp = createWtp(componentReader: customComponentReader, facetReader: customFacetReader)
-
-        then:
-        File componentFile = tmpDir.file("component.xml")
-        File facetFile = tmpDir.file("facet.xml")
-        def (componentReader, facetReader) = getToXmlReaders(wtp)
-        wtp.toXml(componentFile, facetFile)
-        assertXmlIsWritten(componentFile, wtp.orgEclipseWstCommonComponentXml, componentReader)
-        assertXmlIsWritten(facetFile, wtp.orgEclipseWstCommonProjectFacetCoreXml, facetReader)
-    }
-
-    void assertXmlIsWritten(TestFile file, Node xml, Reader reader) {
-        StringWriter stringWriterFileXml = new StringWriter()
-        new XmlNodePrinter(new PrintWriter(stringWriterFileXml)).print(new XmlParser().parse(file))
-        StringWriter stringWriterWrittenXml = new StringWriter()
-        new XmlNodePrinter(new PrintWriter(stringWriterWrittenXml)).print(new XmlParser().parse(reader))
-        StringWriter stringWriterInternalXml = new StringWriter()
-        new XmlNodePrinter(new PrintWriter(stringWriterInternalXml)).print(xml)
-
-        assert stringWriterWrittenXml.toString() == stringWriterInternalXml.toString()
-        assert stringWriterWrittenXml.toString() == stringWriterFileXml.toString()
-    }
-
-    def toXml_shouldContainCustomValues() {
-        def constructorDeployName = 'build'
-        def constructorContextPath = 'contextPath'
-        def constructorWbModuleEntries = [createSomeWbModuleEntry()]
-        def constructorFacets = [createSomeFacet()]
-
-        Wtp wtp = createWtp(wbModuleEntries: constructorWbModuleEntries, facets: constructorFacets,
-                deployName: constructorDeployName, contextPath: constructorContextPath,
-                componentReader: customComponentReader, facetReader: customFacetReader)
-        def (componentReader, facetReader) = getToXmlReaders(wtp)
-
-        when:
-        def wtpFromXml = createWtp(componentReader: componentReader, facetReader: facetReader)
-
-        then:
-        wtp == wtpFromXml
-    }
-
-    def beforeConfigured() {
-        def constructorWbModuleEntries = [createSomeWbModuleEntry()]
-        def constructorFacets = [createSomeFacet()]
-        ActionBroadcast beforeConfiguredActions = new ActionBroadcast()
-        beforeConfiguredActions.add { Wtp wtp ->
-            wtp.wbModuleEntries.clear()
-            wtp.facets.clear()
-        }
-
-        when:
-        Wtp wtp = createWtp(wbModuleEntries: constructorWbModuleEntries, facets: constructorFacets,
-                componentReader: customComponentReader,
-                facetReader: customFacetReader,
-                beforeConfiguredActions: beforeConfiguredActions)
-        def (componentReader, facetReader) = getToXmlReaders(wtp)
-        def wtpFromXml = createWtp(componentReader: componentReader, facetReader: facetReader)
-
-        then:
-        wtpFromXml.wbModuleEntries == constructorWbModuleEntries
-        wtpFromXml.facets == constructorFacets
-    }
-
-    def whenConfigured() {
-        def constructorWbModuleEntry = createSomeWbModuleEntry()
-        def configureActionWbModuleEntry = createSomeWbModuleEntry()
-        configureActionWbModuleEntry.name = configureActionWbModuleEntry.name + 'Other'
-
-        ActionBroadcast whenConfiguredActions = new ActionBroadcast()
-        whenConfiguredActions.add { Wtp wtp ->
-            assert wtp.wbModuleEntries.contains(CUSTOM_WB_MODULE_ENTRIES[0])
-            assert wtp.wbModuleEntries.contains(constructorWbModuleEntry)
-            wtp.wbModuleEntries.add(configureActionWbModuleEntry)
-        }
-
-        when:
-        Wtp wtp = createWtp(wbModuleEntries: [constructorWbModuleEntry], componentReader: customComponentReader,
-                facetReader: customFacetReader, whenConfiguredActions: whenConfiguredActions)
-        def (componentReader, facetReader) = getToXmlReaders(wtp)
-        then:
-        createWtp(componentReader: componentReader, facetReader: facetReader).wbModuleEntries == CUSTOM_WB_MODULE_ENTRIES +
-                ([constructorWbModuleEntry, configureActionWbModuleEntry])
-    }
-
-    def withXml() {
-        ActionBroadcast withXmlActions = new ActionBroadcast()
-        Wtp wtp = createWtp(componentReader: customComponentReader,
-                facetReader: customFacetReader, withXmlActions: withXmlActions)
-
-        when:
-        withXmlActions.add { xmls ->
-            xmls['org.eclipse.wst.commons.component'].'wb-module'.property.find { it. at name == 'context-root' }. at value = 'newValue'
-            xmls['org.eclipse.wst.commons.project.facet.core'].installed.find { it. at facet == 'jst.java' }. at version = '-5x'
-        }
-        def (componentReader, facetReader) = getToXmlReaders(wtp)
-
-        then:
-        new XmlParser().parse(componentReader).'wb-module'.property.find { it. at name == 'context-root' }. at value == 'newValue'
-        new XmlParser().parse(facetReader).installed.find { it. at facet == 'jst.java' }. at version == '-5x'
-    }
-
-    private InputStreamReader getCustomComponentReader() {
-        return new InputStreamReader(getClass().getResourceAsStream('customOrgEclipseWstCommonComponent.xml'))
-    }
-
-    private InputStreamReader getCustomFacetReader() {
-        return new InputStreamReader(getClass().getResourceAsStream('customOrgEclipseWstCommonProjectFacetCoreXml.xml'))
-    }
-
-    private WbProperty createSomeWbModuleEntry() {
-        return new WbProperty('someProp', 'someValue')
-    }
-
-    private Facet createSomeFacet() {
-        return new Facet('someName', '1.0.0')
-    }
-
-    private Wtp createWtp(Map customArgs) {
-        ActionBroadcast dummyBroadcast = new ActionBroadcast()
-        Map args = [wbModuleEntries: [], facets: [], deployName: null, defaultOutput: null, componentReader: null,
-                facetReader: null, beforeConfiguredActions: dummyBroadcast, whenConfiguredActions: dummyBroadcast, withXmlActions: dummyBroadcast] + customArgs
-        EclipseWtp eclipseWtpStub = Mock()
-        eclipseWtpStub.getBeforeConfiguredActions() >> args.beforeConfiguredActions
-        eclipseWtpStub.getWhenConfiguredActions() >> args.whenConfiguredActions
-        eclipseWtpStub.getWithXmlActions() >> args.withXmlActions
-        eclipseWtpStub.getDeployName() >> args.deployName
-        eclipseWtpStub.getContextPath() >> args.contextPath
-        eclipseWtpStub.getFacets() >> args.facets
-        return new Wtp(eclipseWtpStub, args.wbModuleEntries, args.componentReader, args.facetReader)
-    }
-
-    private def getToXmlReaders(Wtp wtp) {
-        StringWriter toComponentXmlText = new StringWriter()
-        StringWriter toFacetXmlText = new StringWriter()
-        wtp.toXml(toComponentXmlText, toFacetXmlText)
-        [new StringReader(toComponentXmlText.toString()), new StringReader(toFacetXmlText.toString())]
-    }
-}
\ No newline at end of file
diff --git a/subprojects/eclipse/src/test/resources/org/gradle/plugins/eclipse/model/customProject.xml b/subprojects/eclipse/src/test/resources/org/gradle/plugins/eclipse/model/customProject.xml
deleted file mode 100644
index 0f0facc..0000000
--- a/subprojects/eclipse/src/test/resources/org/gradle/plugins/eclipse/model/customProject.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-    <name>test</name>
-    <comment>for testing</comment> 
-    <projects>
-        <project>refProject</project>
-    </projects>
-    <buildSpec>
-        <buildCommand>
-            <name>org.eclipse.jdt.core.scalabuilder</name>
-            <arguments>
-                <dictionary>
-                    <key>climate</key>
-                    <value>cold</value>
-                </dictionary>
-            </arguments>
-        </buildCommand>
-    </buildSpec>
-    <natures>
-        <nature>org.eclipse.jdt.core.scalanature</nature>
-    </natures>
-    <links>
-        <link>
-            <name>somename</name>
-            <type>sometype</type>
-            <location>somelocation</location>
-        </link>
-    </links>
-</projectDescription>
\ No newline at end of file
diff --git a/subprojects/ide/ide.gradle b/subprojects/ide/ide.gradle
new file mode 100644
index 0000000..d8c3403
--- /dev/null
+++ b/subprojects/ide/ide.gradle
@@ -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.
+ */
+apply plugin: 'groovy' //needed so that resources are excluded from compilation (see how ideaProject task is configured in root project)
+apply from: "$rootDir/gradle/integTest.gradle"
+
+dependencies {
+    groovy libraries.groovy_depends
+
+    compile project(':scala')
+    compile project(':core')
+    compile project(':plugins')
+    compile project(':toolingApi')
+    compile libraries.commons_io
+    compile libraries.slf4j_api, libraries.jaxen
+    compile 'dom4j:dom4j:1.6.1 at jar'
+
+    testCompile project(path: ':core', configuration: 'testFixtures')
+    testCompile libraries.xmlunit
+    testRuntime project(path: ':core', configuration: 'testFixturesRuntime')
+
+    //needed?
+    integTestCompile libraries.slf4j_api
+    integTestCompile project(path: ':core', configuration: 'integTestFixtures')
+    integTestRuntime project(path: ':core', configuration: 'integTestFixturesRuntime')
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AbstractIdeIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AbstractIdeIntegrationTest.groovy
new file mode 100644
index 0000000..d073477
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AbstractIdeIntegrationTest.groovy
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide
+
+import org.gradle.integtests.fixtures.MavenRepository
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest
+import org.gradle.util.TestFile
+
+abstract class AbstractIdeIntegrationTest extends AbstractIntegrationTest {
+    protected void runTask(taskName, settingsScript = "rootProject.name = 'root'", buildScript) {
+        def settingsFile = file("settings.gradle")
+        settingsFile << settingsScript
+
+        def buildFile = file("build.gradle")
+        buildFile << buildScript
+
+        executer.usingSettingsFile(settingsFile).usingBuildScript(buildFile).withTasks(taskName).run()
+    }
+
+    protected File getFile(Map options, String filename) {
+        def file = options?.project ? file(options.project, filename) : file(filename)
+        if (options?.print) { println file.text }
+        file
+    }
+
+    protected parseFile(Map options, String filename) {
+        def file = getFile(options, filename)
+        new XmlSlurper().parse(file)
+    }
+
+    protected void createJavaSourceDirs(TestFile buildFile) {
+        buildFile.parentFile.file("src/main/java").createDir()
+        buildFile.parentFile.file("src/main/resources").createDir()
+    }
+
+    protected File publishArtifact(dir, group, artifact, dependency = null) {
+        def module = new MavenRepository(new TestFile(dir)).module(group, artifact, 1.0)
+        if (dependency) {
+            module.dependsOn(dependency)
+        }
+        return module.publishArtifact()
+    }
+
+    protected runIdeaTask(buildScript) {
+        runTask("idea", buildScript)
+    }
+
+    protected parseImlFile(Map options = [:], String projectName) {
+        parseFile(options, "${projectName}.iml")
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AutoTestedSamplesTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AutoTestedSamplesTest.groovy
new file mode 100644
index 0000000..71bf480
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AutoTestedSamplesTest.groovy
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide
+
+import org.gradle.integtests.fixtures.internal.AbstractAutoTestedSamplesTest
+import org.junit.Test
+
+/**
+ * Author: Szczepan Faber, created at: 3/27/11
+ */
+class AutoTestedSamplesTest extends AbstractAutoTestedSamplesTest {
+
+    @Test
+    void runSamples() {
+        runSamplesFrom("subprojects/ide/src/main")
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/AbstractEclipseIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/AbstractEclipseIntegrationTest.groovy
new file mode 100644
index 0000000..357a529
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/AbstractEclipseIntegrationTest.groovy
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.eclipse
+
+import org.gradle.plugins.ide.AbstractIdeIntegrationTest
+
+class AbstractEclipseIntegrationTest extends AbstractIdeIntegrationTest {
+    protected runEclipseTask(settingsScript = "rootProject.name = 'root'", buildScript) {
+        runTask("eclipse", settingsScript, buildScript)
+    }
+
+    protected File getProjectFile(Map options) {
+        getFile(options, ".project")
+    }
+
+    protected File getClasspathFile(Map options) {
+        getFile(options, ".classpath")
+    }
+
+    protected File getComponentFile(Map options) {
+        getFile(options, ".settings/org.eclipse.wst.common.component")
+    }
+
+    protected File getFacetFile(Map options) {
+        getFile(options, ".settings/org.eclipse.wst.common.project.facet.core.xml")
+    }
+
+    protected parseProjectFile(Map options) {
+        parseFile(options, ".project")
+    }
+
+    protected parseClasspathFile(Map options) {
+        parseFile(options, ".classpath")
+    }
+
+    protected parseComponentFile(Map options) {
+        parseFile(options, ".settings/org.eclipse.wst.common.component")
+    }
+
+    protected parseFacetFile(Map options) {
+        parseFile(options, ".settings/org.eclipse.wst.common.project.facet.core.xml")
+    }
+
+    protected String parseJdtFile() {
+        return getFile([:], '.settings/org.eclipse.jdt.core.prefs').text
+    }
+
+    protected findEntries(classpath, kind) {
+        classpath.classpathentry.findAll { it. at kind == kind }
+    }
+
+    protected libEntriesInClasspathFileHaveFilenames(String... filenames) {
+        def classpath = parseClasspathFile()
+        def libs = findEntries(classpath, "lib")
+        assert libs*. at path*.text().collect { new File(it).name } as Set == filenames as Set
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseClasspathIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseClasspathIntegrationTest.groovy
new file mode 100644
index 0000000..4fb5d49
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseClasspathIntegrationTest.groovy
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.eclipse
+
+import org.gradle.integtests.fixtures.TestResources
+import org.junit.Rule
+import org.junit.Test
+
+/**
+ * Author: Szczepan Faber
+ */
+class EclipseClasspathIntegrationTest extends AbstractEclipseIntegrationTest {
+
+    @Rule
+    public final TestResources testResources = new TestResources()
+
+    String content
+
+    @Test
+    void allowsConfiguringEclipseClasspath() {
+        //when
+        runEclipseTask """
+apply plugin: 'java'
+apply plugin: 'eclipse'
+
+sourceSets.main.java.srcDirs.each { it.mkdirs() }
+sourceSets.main.resources.srcDirs.each { it.mkdirs() }
+
+configurations {
+  someConfig
+  someOtherConfig
+}
+
+dependencies {
+  someConfig files('foo.txt', 'bar.txt', 'baz.txt')
+  someOtherConfig files('baz.txt')
+}
+
+eclipse {
+
+  pathVariables fooPathVariable: new File('.')
+
+  classpath {
+    sourceSets = []
+
+    plusConfigurations += configurations.someConfig
+    minusConfigurations += configurations.someOtherConfig
+
+    containers 'someFriendlyContainer', 'andYetAnotherContainer'
+
+    classesOutputDir = file('build-eclipse')
+
+    downloadSources = false
+    downloadJavadoc = true
+  }
+}
+"""
+
+        content = getFile([:], '.classpath').text
+        println content
+
+        //then
+        contains('foo.txt')
+        contains('bar.txt')
+
+        contains('fooPathVariable')
+        contains('someFriendlyContainer', 'andYetAnotherContainer')
+
+        contains('build-eclipse')
+    }
+
+    protected def contains(String ... contents) {
+        contents.each { assert content.contains(it)}
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest.groovy
new file mode 100644
index 0000000..94216d2
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest.groovy
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.eclipse
+
+import org.gradle.integtests.fixtures.TestResources
+import org.junit.Rule
+import org.junit.Test
+
+class EclipseIntegrationTest extends AbstractEclipseIntegrationTest {
+    private static String nonAscii = "\\u7777\\u8888\\u9999"
+
+    @Rule
+    public final TestResources testResources = new TestResources()
+
+    @Test
+    void canCreateAndDeleteMetaData() {
+        File buildFile = testFile("master/build.gradle")
+        usingBuildFile(buildFile).run()
+    }
+
+    @Test
+    void sourceEntriesInClasspathFileAreSortedAsPerUsualConvention() {
+        def expectedOrder = [
+                "src/main/java",
+                "src/main/groovy",
+                "src/main/resources",
+                "src/test/java",
+                "src/test/groovy",
+                "src/test/resources",
+                "src/integTest/java",
+                "src/integTest/groovy",
+                "src/integTest/resources"
+        ]
+
+        expectedOrder.each { testFile(it).mkdirs() }
+
+        runEclipseTask """
+apply plugin: "java"
+apply plugin: "groovy"
+apply plugin: "eclipse"
+
+sourceSets {
+    integTest {
+        resources { srcDir "src/integTest/resources" }
+        java { srcDir "src/integTest/java" }
+        groovy { srcDir "src/integTest/groovy" }
+    }
+}
+        """
+
+        def classpath = parseClasspathFile()
+        def sourceEntries = findEntries(classpath, "src")
+        assert sourceEntries*. at path == expectedOrder
+    }
+
+    @Test
+    void outputDirDefaultsToEclipseDefault() {
+        runEclipseTask("apply plugin: 'java'; apply plugin: 'eclipse'")
+
+        def classpath = parseClasspathFile()
+
+        def outputs = findEntries(classpath, "output")
+        assert outputs*. at path == ["bin"]
+
+        def sources = findEntries(classpath, "src")
+        sources.each { assert !it.attributes().containsKey("path") }
+    }
+
+    @Test
+    void canHandleCircularModuleDependencies() {
+        def repoDir = file("repo")
+        def artifact1 = publishArtifact(repoDir, "myGroup", "myArtifact1", "myArtifact2")
+        def artifact2 = publishArtifact(repoDir, "myGroup", "myArtifact2", "myArtifact1")
+
+        runEclipseTask """
+apply plugin: "java"
+apply plugin: "eclipse"
+
+repositories {
+    mavenRepo urls: "${repoDir.toURI()}"
+}
+
+dependencies {
+    compile "myGroup:myArtifact1:1.0"
+}
+        """
+
+        libEntriesInClasspathFileHaveFilenames(artifact1.name, artifact2.name)
+    }
+
+    @Test
+    void eclipseFilesAreWrittenWithUtf8Encoding() {
+        runEclipseTask """
+apply plugin: "war"
+apply plugin: "eclipse"
+
+eclipseProject {
+  projectName = "$nonAscii"
+}
+
+eclipseClasspath {
+  containers("$nonAscii")
+}
+
+eclipseWtpComponent {
+  deployName = "$nonAscii"
+}
+
+eclipseWtpFacet {
+  facet([name: "$nonAscii"])
+}
+        """
+
+        checkIsWrittenWithUtf8Encoding(getProjectFile())
+        checkIsWrittenWithUtf8Encoding(getClasspathFile())
+        checkIsWrittenWithUtf8Encoding(getComponentFile())
+        checkIsWrittenWithUtf8Encoding(getFacetFile())
+    }
+
+    @Test
+    void triggersBeforeAndWhenConfigurationHooks() {
+        //this test is a bit peculiar as it has assertions inside the gradle script
+        //couldn't find a better way of asserting on before/when configured hooks
+        runEclipseTask('''
+apply plugin: 'java'
+apply plugin: 'war'
+apply plugin: 'eclipse'
+
+def beforeConfiguredObjects = 0
+def whenConfiguredObjects = 0
+
+eclipseProject {
+    beforeConfigured { beforeConfiguredObjects++ }
+    whenConfigured { whenConfiguredObjects++ }
+}
+eclipseClasspath {
+    beforeConfigured { beforeConfiguredObjects++ }
+    whenConfigured { whenConfiguredObjects++ }
+}
+eclipseWtpFacet {
+    beforeConfigured { beforeConfiguredObjects++ }
+    whenConfigured { whenConfiguredObjects++ }
+}
+eclipseWtpComponent {
+    beforeConfigured { beforeConfiguredObjects++ }
+    whenConfigured { whenConfiguredObjects++ }
+}
+eclipseJdt {
+    beforeConfigured { beforeConfiguredObjects++ }
+    whenConfigured { whenConfiguredObjects++ }
+}
+
+eclipse << {
+    assert beforeConfiguredObjects == 5 : "beforeConfigured() hooks shoold be fired for domain model objects"
+    assert whenConfiguredObjects == 5 : "whenConfigured() hooks shoold be fired for domain model objects"
+}
+''')
+
+    }
+
+    @Test
+    void respectsPerConfigurationExcludes() {
+        def repoDir = file("repo")
+        def artifact1 = publishArtifact(repoDir, "myGroup", "myArtifact1", "myArtifact2")
+        def artifact2 = publishArtifact(repoDir, "myGroup", "myArtifact2")
+
+        runEclipseTask """
+apply plugin: 'java'
+apply plugin: 'eclipse'
+
+repositories {
+    mavenRepo urls: "${repoDir.toURI()}"
+}
+
+configurations {
+    compile.exclude module: 'myArtifact2'
+}
+
+dependencies {
+    compile "myGroup:myArtifact1:1.0"
+}
+        """
+
+        libEntriesInClasspathFileHaveFilenames(artifact1.name)
+    }
+
+    @Test
+    void respectsPerDependencyExcludes() {
+        def repoDir = file("repo")
+        def artifact1 = publishArtifact(repoDir, "myGroup", "myArtifact1", "myArtifact2")
+        def artifact2 = publishArtifact(repoDir, "myGroup", "myArtifact2")
+
+        runEclipseTask """
+apply plugin: 'java'
+apply plugin: 'eclipse'
+
+repositories {
+    mavenRepo urls: "${repoDir.toURI()}"
+}
+
+dependencies {
+    compile("myGroup:myArtifact1:1.0") {
+        exclude module: "myArtifact2"
+    }
+}
+        """
+
+        libEntriesInClasspathFileHaveFilenames(artifact1.name)
+    }
+
+    private void checkIsWrittenWithUtf8Encoding(File file) {
+        def text = file.getText("UTF-8")
+        assert text.contains('encoding="UTF-8"')
+        String expectedNonAsciiChars = "\u7777\u8888\u9999"
+        assert text.contains(expectedNonAsciiChars)
+    }
+
+    @Test
+    void addsLinkToTheOutputFile() {
+        runEclipseTask '''
+apply plugin: 'java'
+apply plugin: 'eclipse'
+
+eclipseProject {
+    link name: 'one', type: '2', location: '/xyz'
+    link name: 'two', type: '3', locationUri: 'file://xyz'
+}
+'''
+
+        println getProjectFile().text
+        def xml = parseProjectFile()
+        assert xml.linkedResources.link[0].name.text() == 'one'
+        assert xml.linkedResources.link[0].type.text() == '2'
+        assert xml.linkedResources.link[0].location.text() == '/xyz'
+
+        assert xml.linkedResources.link[1].name.text() == 'two'
+        assert xml.linkedResources.link[1].type.text() == '3'
+        assert xml.linkedResources.link[1].locationURI.text() == 'file://xyz'
+    }
+
+    @Test
+    void allowsConfiguringJavaVersionWithSimpleTypes() {
+        runEclipseTask '''
+apply plugin: 'java'
+apply plugin: 'eclipse'
+
+eclipseJdt {
+    sourceCompatibility = '1.4'
+    targetCompatibility = 1.3
+}
+'''
+
+        def jdt = parseJdtFile()
+        assert jdt.contains('source=1.4')
+        assert jdt.contains('targetPlatform=1.3')
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseMultiModuleIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseMultiModuleIntegrationTest.groovy
new file mode 100644
index 0000000..33951ed
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseMultiModuleIntegrationTest.groovy
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.eclipse
+
+import org.gradle.integtests.fixtures.TestResources
+import org.junit.Rule
+import org.junit.Test
+import org.gradle.plugins.ide.AbstractIdeIntegrationTest
+
+/**
+ * @author Szczepan Faber, @date 01.03.11
+ */
+class EclipseMultiModuleIntegrationTest extends AbstractIdeIntegrationTest {
+    @Rule
+    public final TestResources testResources = new TestResources()
+
+    @Test
+    void dealsWithDuplicatedModuleNames() {
+      /*
+      This is the multi-module project structure the integration test works with:
+      -root
+        -api
+        -shared
+          -api
+          -model
+        -services
+          -util
+        -util
+        -contrib
+          -services
+            -utilities (renamed by user to 'util'
+      */
+
+        def settingsFile = file("master/settings.gradle")
+        settingsFile << """
+include 'api'
+include 'shared:api', 'shared:model'
+include 'services:utilities'
+include 'util'
+include 'contrib:services:util'
+        """
+
+        def buildFile = file("master/build.gradle")
+        buildFile << """
+allprojects {
+    apply plugin: 'java'
+    apply plugin: 'eclipse'
+}
+
+project(':api') {
+    dependencies {
+        compile project(':shared:api'), project(':shared:model')
+    }
+}
+
+project(':shared:model') {
+    eclipseProject {
+        projectName = 'very-cool-model'
+    }
+}
+
+project(':services:utilities') {
+    dependencies {
+        compile project(':util'), project(':contrib:services:util'), project(':shared:api'), project(':shared:model')
+    }
+    eclipseProject {
+        projectName = 'util'
+    }
+}
+"""
+
+        //when
+        executer.usingBuildScript(buildFile).usingSettingsFile(settingsFile).withTasks("eclipse").run()
+//        println(getTestDir())
+
+        //then
+        assertApiProjectContainsCorrectDependencies()
+        assertServicesUtilProjectContainsCorrectDependencies()
+    }
+
+    def assertServicesUtilProjectContainsCorrectDependencies() {
+        List deps = parseEclipseProjectDependencies(project: 'master/services/utilities')
+
+        assert deps.contains("/very-cool-model")
+        assert deps.contains("/util")
+        assert deps.contains("/shared-api")
+        assert deps.contains("/contrib-services-util")
+    }
+
+    def assertApiProjectContainsCorrectDependencies() {
+        def deps = parseEclipseProjectDependencies(project: 'master/api')
+
+        assert deps.contains("/very-cool-model")
+        assert deps.contains("/shared-api")
+    }
+
+    @Test
+    void shouldCreateCorrectClasspathEvenIfUserReconfiguresTheProjectName() {
+        //use case from the mailing list
+        def settingsFile = file("master/settings.gradle")
+        settingsFile << "include 'api', 'shared:model', 'nonEclipse'"
+
+        def buildFile = file("master/build.gradle")
+        buildFile << """
+allprojects {
+    apply plugin: 'java'
+    if (project.name != 'nonEclipse') {
+        apply plugin: 'eclipse'
+    }
+}
+
+subprojects {
+    eclipseProject {
+        projectName = rootProject.name + project.path.replace(':', '-')
+    }
+}
+
+project(':api') {
+    dependencies {
+        //let's add a nonEclipse project to stress the test
+        compile project(':shared:model'), project(':nonEclipse')
+    }
+}
+"""
+
+        //when
+        executer.usingBuildScript(buildFile).usingSettingsFile(settingsFile).withTasks("eclipse").run()
+
+        //then
+        def deps = parseEclipseProjectDependencies(project: 'master/api')
+
+        assert deps.contains("/master-shared-model")
+        assert deps.contains("/nonEclipse")
+    }
+
+    List parseEclipseProjectDependencies(def options) {
+        def eclipseClasspathFile = parseFile(options, ".classpath")
+        def deps = eclipseClasspathFile.classpathentry.findAll { it. at kind.text() == 'src' }.collect { it. at path.text() }
+        return deps
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseProjectIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseProjectIntegrationTest.groovy
new file mode 100644
index 0000000..b4727d5
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseProjectIntegrationTest.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.plugins.ide.eclipse
+
+import org.gradle.integtests.fixtures.TestResources
+import org.junit.Rule
+import org.junit.Test
+
+/**
+ * Author: Szczepan Faber
+ */
+class EclipseProjectIntegrationTest extends AbstractEclipseIntegrationTest {
+
+    @Rule
+    public final TestResources testResources = new TestResources()
+
+    String content
+
+    @Test
+    void allowsConfiguringEclipseProject() {
+        //when
+        runEclipseTask """
+apply plugin: 'java'
+apply plugin: 'eclipse'
+
+eclipse {
+  project {
+    name = 'someBetterName'
+    comment = 'a test project'
+
+    referencedProjects = ['some referenced project'] as Set
+    referencedProjects 'some cool project'
+
+    natures = ['test.groovy.nature']
+    natures 'test.java.nature'
+
+    buildCommand 'buildThisLovelyProject'
+    buildCommand argumentFoo: 'a foo argument', 'buildWithTheArguments'
+
+    linkedResource name: 'linkToFolderFoo', type: 'aFolderFoo', location: '/test/folders/foo'
+    linkedResource name: 'linkToUriFoo', type: 'aFooUri', locationUri: 'http://test/uri/foo'
+  }
+
+  jdt {
+    sourceCompatibility = 1.4
+    targetCompatibility = 1.3
+  }
+}
+        """
+
+        //then
+        content = getFile([:], '.project').text
+        println content
+
+        def dotProject = parseProjectFile()
+        assert dotProject.name.text() == 'someBetterName'
+        assert dotProject.comment.text() == 'a test project'
+
+        contains('some referenced project', 'some cool project')
+        contains('test.java.nature', 'test.groovy.nature')
+        contains('buildThisLovelyProject', 'argumentFoo', 'a foo argument', 'buildWithTheArguments')
+
+        contains('linkToFolderFoo', 'aFolderFoo', '/test/folders/foo')
+        contains('linkToUriFoo', 'aFooUri', 'http://test/uri/foo')
+
+        def jdt = parseJdtFile()
+        assert jdt.contains('targetPlatform=1.3')
+        assert jdt.contains('source=1.4')
+    }
+
+    protected def contains(String ... contents) {
+        contents.each { assert content.contains(it)}
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpIntegrationTest.groovy
new file mode 100644
index 0000000..bafc970
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpIntegrationTest.groovy
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.eclipse
+
+import org.junit.Test
+
+// TODO: run prepareWebProject() only once per class for performance reasons (not as simply as it seems)
+class EclipseWtpIntegrationTest extends AbstractEclipseIntegrationTest {
+    @Test
+    void projectDependenciesOfWebProjectAreMarkedAsJstUtilityProjects() {
+        prepareWebProject()
+
+        hasUtilityFacet("java1")
+        hasUtilityFacet("java2")
+        hasUtilityFacet("groovy")
+    }
+
+    @Test
+    void projectDependenciesOfWebProjectHaveNecessaryNaturesAdded() {
+        prepareWebProject()
+
+        hasNecessaryNaturesAdded("java1")
+        hasNecessaryNaturesAdded("java2")
+        hasNecessaryNaturesAdded("groovy")
+    }
+
+    @Test
+    void projectDependenciesOfWebProjectHaveNecessaryBuildersAdded() {
+        prepareWebProject()
+
+        hasNecessaryBuildersAdded("java1")
+        hasNecessaryBuildersAdded("java2")
+        hasNecessaryBuildersAdded("groovy")
+    }
+
+    @Test
+    void projectDependenciesOfWebProjectHaveTrimmedDownComponentSettingsFile() {
+        prepareWebProject()
+
+        hasTrimmedDownComponentSettingsFile("java1", "src/main/java", "src/main/resources")
+        hasTrimmedDownComponentSettingsFile("java2", "src/main/java", "src/main/resources")
+        hasTrimmedDownComponentSettingsFile("groovy", "src/main/java", "src/main/groovy", "src/main/resources")
+    }
+
+    @Test
+    void jarDependenciesOfUtilityProjectsAreFlaggedAsRuntimeDependency() {
+        prepareWebProject()
+
+        def classpath = parseClasspathFile(project: "java1")
+
+        def firstLevelDep = classpath.classpathentry.find { it. at path.text().endsWith("myartifact-1.0.jar") }
+        assert firstLevelDep.attributes.attribute.find { it. at name.text() == "org.eclipse.jst.component.dependency" }
+
+        def secondLevelDep = classpath.classpathentry.find { it. at path.text().endsWith("myartifactdep-1.0.jar") }
+        assert secondLevelDep.attributes.attribute.find { it. at name.text() == "org.eclipse.jst.component.dependency" }
+
+    }
+
+    @Test
+    void allProjectDependenciesOfWebProjectAreAddedAsRuntimeDependencies() {
+        prepareWebProject()
+
+        def projectModules = parseComponentFile(project: "web")
+
+		assert getDeployName(projectModules) == "web"
+		assert getHandleFilenames(projectModules) == ["java1", "java2", "groovy", "myartifact-1.0.jar", "myartifactdep-1.0.jar"] as Set
+		assert getDependencyTypes(projectModules) == ["uses"] * 5 as Set
+    }
+
+    private prepareWebProject() {
+        def repoDir = file("repo")
+        publishArtifact(repoDir, "mygroup", "myartifact", "myartifactdep")
+        publishArtifact(repoDir, "mygroup", "myartifactdep")
+
+        def settingsFile = file("settings.gradle")
+        settingsFile << """
+include("web")
+include("java1")
+include("java2")
+include("groovy")
+        """
+
+        def webBuildFile = getFile(project: "web", "build.gradle")
+        createJavaSourceDirs(webBuildFile)
+        webBuildFile.parentFile.file("src/main/webapp").createDir()
+
+        webBuildFile << """
+apply plugin: "eclipse"
+apply plugin: "war"
+
+repositories {
+    mavenRepo(name: "repo", urls: "${repoDir.toURI()}")
+}
+
+dependencies {
+    compile project(":java1")
+    compile project(":groovy")
+    runtime "mygroup:myartifact:1.0"
+}
+        """
+
+        def java1BuildFile = getFile(project: "java1", "build.gradle")
+        createJavaSourceDirs(java1BuildFile)
+
+        java1BuildFile << """
+apply plugin: "eclipse"
+apply plugin: "java"
+
+repositories {
+    mavenRepo(name: "repo", urls: "${repoDir.toURI()}")
+}
+
+dependencies {
+    compile project(":java2")
+    runtime "mygroup:myartifact:1.0"
+}
+        """
+
+        def java2BuildFile = getFile(project: "java2", "build.gradle")
+        createJavaSourceDirs(java2BuildFile)
+
+        java2BuildFile << """
+apply plugin: "eclipse"
+apply plugin: "java"
+
+repositories {
+    mavenRepo(name: "repo", urls: "${repoDir.toURI()}")
+}
+
+dependencies {
+    runtime "mygroup:myartifact:1.0"
+}
+        """
+
+        def groovyBuildFile = getFile(project: "groovy", "build.gradle")
+        createJavaSourceDirs(groovyBuildFile)
+        groovyBuildFile.parentFile.file("src/main/groovy").createDir()
+
+        groovyBuildFile << """
+apply plugin: "eclipse"
+apply plugin: "groovy"
+        """
+
+        executer.usingSettingsFile(settingsFile).withTasks("eclipse").run()
+    }
+
+    private void hasUtilityFacet(String project) {
+        def file = getFacetFile(project: project)
+        def facetedProject = new XmlSlurper().parse(file)
+        assert facetedProject.children().any { it. at facet.text() == "jst.utility" && it. at version.text() == "1.0" }
+    }
+
+    private void hasNecessaryBuildersAdded(String project) {
+        def projectDescription = parseProjectFile(project: project)
+        assert projectDescription.buildSpec.buildCommand.name*.text().containsAll(
+                ["org.eclipse.wst.common.project.facet.core.builder", "org.eclipse.wst.validation.validationbuilder"])
+    }
+
+    private void hasNecessaryNaturesAdded(String project) {
+        def projectDescription = parseProjectFile(project: project)
+        assert projectDescription.natures.nature*.text().containsAll(["org.eclipse.wst.common.project.facet.core.nature",
+                "org.eclipse.jem.workbench.JavaEMFNature", "org.eclipse.wst.common.modulecore.ModuleCoreNature"])
+    }
+
+    private void hasTrimmedDownComponentSettingsFile(String projectName, String... sourcePaths) {
+        def projectModules = parseComponentFile(project: projectName, print: true)
+
+        assert getDeployName(projectModules) == projectName
+        assert getSourcePaths(projectModules) == sourcePaths as Set
+        assert getDeployPaths(projectModules) == ["/"] * sourcePaths.size() as Set
+        assert getHandleFilenames(projectModules) == [] as Set
+        assert getDependencyTypes(projectModules) == [] as Set
+    }
+
+    private String getDeployName(projectModules) {
+		def names = projectModules."wb-module".@"deploy-name"*.text()
+        assert names.size() == 1
+        names[0]
+	}
+
+    private Set getSourcePaths(projectModules) {
+        projectModules."wb-module"."wb-resource".@"source-path"*.text() as Set
+    }
+
+    private Set getDeployPaths(projectModules) {
+        projectModules."wb-module"."wb-resource".@"deploy-path"*.text() as Set
+    }
+
+	private Set getHandleFilenames(projectModules) {
+		projectModules."wb-module"."dependent-module". at handle*.text().collect { it.substring(it.lastIndexOf("/") + 1) } as Set
+	}
+
+	private Set getDependencyTypes(projectModules) {
+		projectModules."wb-module"."dependent-module"."dependency-type"*.text() as Set
+	}
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpModelIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpModelIntegrationTest.groovy
new file mode 100644
index 0000000..d96512d
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpModelIntegrationTest.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.plugins.ide.eclipse
+
+import org.gradle.integtests.fixtures.TestResources
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+/**
+ * Author: Szczepan Faber, created at: 4/19/11
+ */
+class EclipseWtpModelIntegrationTest extends AbstractEclipseIntegrationTest {
+
+    @Rule
+    public final TestResources testResources = new TestResources()
+
+    String component
+
+    @Test
+    void allowsConfiguringEclipseProject() {
+        //given
+        file('someExtraSourceDir').mkdirs()
+
+        def repoDir = file("repo")
+        publishArtifact(repoDir, "gradle", "foo")
+        publishArtifact(repoDir, "gradle", "bar")
+        publishArtifact(repoDir, "gradle", "baz")
+
+
+        //when
+        runEclipseTask """
+apply plugin: 'java'
+apply plugin: 'war'
+apply plugin: 'eclipse'
+
+configurations {
+  configOne
+  configTwo
+}
+
+repositories {
+  mavenRepo urls: "${repoDir.toURI()}"
+}
+
+dependencies {
+  configOne 'gradle:foo:1.0', 'gradle:bar:1.0', 'gradle:baz:1.0'
+  configTwo 'gradle:baz:1.0'
+}
+
+eclipse {
+
+  pathVariables 'userHomeVariable' : file(System.properties['user.home'])
+
+  wtp {
+    component {
+      contextPath = 'killerApp'
+
+      sourceDirs += file('someExtraSourceDir')
+
+      plusConfigurations += configurations.configOne
+      minusConfigurations += configurations.configTwo
+
+      deployName = 'someBetterDeployName'
+
+      resource sourcePath: './src/foo/bar', deployPath: './deploy/foo/bar'
+
+      property name: 'wbPropertyOne', value: 'New York!'
+    }
+    facet {
+      facet name: 'gradleFacet', version: '1.333'
+    }
+  }
+}
+        """
+
+        component = getFile([:], '.settings/org.eclipse.wst.common.component').text
+        def facet = getFile([:], '.settings/org.eclipse.wst.common.project.facet.core.xml').text
+        println facet //TODO SF after completing the refactoring, get rid of the printlns
+
+        //then component:
+        contains('someExtraSourceDir')
+
+        contains('foo-1.0.jar', 'bar-1.0.jar')
+        assert !component.contains('baz-1.0.jar')
+
+        contains('someBetterDeployName')
+
+        //contains('userHomeVariable') //TODO SF don't know how to test it at the moment
+
+        contains('./src/foo/bar', './deploy/foo/bar')
+        contains('wbPropertyOne', 'New York!')
+
+        contains('killerApp')
+
+        assert facet.contains('gradleFacet')
+        assert facet.contains('1.333')
+    }
+
+    @Ignore("GRADLE-1487")
+    @Test
+    void allowsFileDependencies() {
+        //when
+        runEclipseTask """
+apply plugin: 'java'
+apply plugin: 'war'
+apply plugin: 'eclipse'
+
+configurations {
+  configOne
+  configTwo
+}
+
+dependencies {
+  configOne files('foo.txt', 'bar.txt', 'baz.txt')
+  configTwo files('baz.txt')
+}
+
+eclipse {
+  wtp {
+    plusConfigurations += configurations.configOne
+    minusConfigurations += configurations.configTwo
+  }
+}
+        """
+
+        def component = getFile([:], '.settings/org.eclipse.wst.common.component').text
+        println component
+
+        //then
+    }
+
+    @Test
+    void createsTasksOnDependantUponProjectsEvenIfTheyDontHaveWarPlugin() {
+        //given
+        def settings = file('settings.gradle')
+        settings << "include 'impl', 'contrib'"
+
+        def build = file('build.gradle')
+        build << """
+project(':impl') {
+  apply plugin: 'java'
+  apply plugin: 'war'
+  apply plugin: 'eclipse'
+
+  dependencies { compile project(':contrib') }
+}
+
+project(':contrib') {
+  apply plugin: 'java'
+  apply plugin: 'eclipse'
+}
+"""
+        //when
+        executer.usingSettingsFile(settings).usingBuildScript(build).withTasks('eclipse').run()
+
+        //then
+        getComponentFile(project: 'impl')
+        getFacetFile(project: 'impl')
+
+        getComponentFile(project: 'contrib')
+        getFacetFile(project: 'contrib')
+    }
+
+    protected def contains(String ... contents) {
+        contents.each { assert component.contains(it)}
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/ConfigurationHooksTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/ConfigurationHooksTest.groovy
new file mode 100644
index 0000000..493683f
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/ConfigurationHooksTest.groovy
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.idea
+
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.plugins.ide.AbstractIdeIntegrationTest
+import org.junit.Rule
+import org.junit.Test
+
+class ConfigurationHooksTest extends AbstractIdeIntegrationTest {
+    @Rule
+    public final TestResources testResources = new TestResources()
+
+    @Test
+    void triggersBeforeAndWhenConfigurationHooks() {
+        //this test is a bit peculiar as it has assertions inside the gradle script
+        //couldn't find a better way of asserting on before/when configured hooks
+        runIdeaTask '''
+apply plugin: 'java'
+apply plugin: 'idea'
+
+def beforeConfiguredObjects = 0
+def whenConfiguredObjects = 0
+
+ideaModule {
+    beforeConfigured { beforeConfiguredObjects++ }
+    whenConfigured { whenConfiguredObjects++ }
+}
+ideaProject {
+    beforeConfigured { beforeConfiguredObjects++ }
+    whenConfigured { whenConfiguredObjects++ }
+}
+ideaWorkspace {
+    beforeConfigured { beforeConfiguredObjects++ }
+    whenConfigured { whenConfiguredObjects++ }
+}
+
+idea << {
+    assert beforeConfiguredObjects == 3 : "beforeConfigured() hooks shoold be fired for domain model objects"
+    assert whenConfiguredObjects == 3 : "whenConfigured() hooks shoold be fired for domain model objects"
+}
+'''
+    }
+
+    @Test
+    void whenHooksApplyChangesToGeneratedFile() {
+        //when
+        runIdeaTask '''
+apply plugin: 'java'
+apply plugin: 'idea'
+
+ideaModule {
+    whenConfigured { it.javaVersion = '1.44' }
+}
+ideaProject {
+    whenConfigured { it.wildcards += '!?*.ruby' }
+}
+'''
+        //then
+        def iml = getFile([:], 'root.iml').text
+        assert iml.contains('1.44')
+
+        def ipr = getFile([:], 'root.ipr').text
+        assert ipr.contains('!?*.ruby')
+    }
+
+
+    private containsDir(path, urls) {
+        urls.any { it.endsWith(path) }
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaIntegrationTest.groovy
new file mode 100644
index 0000000..f5b46c1
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaIntegrationTest.groovy
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.idea
+
+import junit.framework.AssertionFailedError
+import org.custommonkey.xmlunit.Diff
+import org.custommonkey.xmlunit.ElementNameAndAttributeQualifier
+import org.custommonkey.xmlunit.XMLAssert
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.plugins.ide.AbstractIdeIntegrationTest
+import org.gradle.util.TestFile
+import org.junit.Rule
+import org.junit.Test
+
+class IdeaIntegrationTest extends AbstractIdeIntegrationTest {
+    @Rule
+    public final TestResources testResources = new TestResources()
+
+    @Test
+    void mergesImlCorrectly() {
+        def buildFile = file("master/build.gradle")
+        buildFile << """
+apply plugin: 'java'
+apply plugin: 'idea'
+"""
+
+        //given
+        executer.usingBuildScript(buildFile).withTasks('idea').run()
+        def fileContent = getFile([:], 'master/master.iml').text
+
+        executer.usingBuildScript(buildFile).withTasks('idea').run()
+        def contentAfterMerge = getFile([:], 'master/master.iml').text
+
+        //then
+        assert fileContent == contentAfterMerge
+    }
+
+    @Test
+    void canCreateAndDeleteMetaData() {
+        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
+    void worksWithAnEmptyProject() {
+        executer.withTasks('idea').run()
+
+        assertHasExpectedContents('root.ipr')
+        assertHasExpectedContents('root.iml')
+    }
+
+    @Test
+    void worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied() {
+        executer.withTasks('idea').run()
+
+        assertHasExpectedContents('root.ipr')
+    }
+
+    @Test
+    void worksWithNonStandardLayout() {
+        executer.inDirectory(testDir.file('root')).withTasks('idea').run()
+
+        assertHasExpectedContents('root/root.ipr')
+        assertHasExpectedContents('root/root.iml')
+        assertHasExpectedContents('top-level.iml')
+    }
+
+    @Test
+    void overwritesExistingDependencies() {
+        executer.withTasks('idea').run()
+
+        assertHasExpectedContents('root.iml')
+    }
+
+    @Test
+    void outputDirsDefaultToToIdeaDefaults() {
+        runIdeaTask("apply plugin: 'java'; apply plugin: 'idea'")
+
+        def module = parseImlFile("root")
+        assert module.component.@"inherit-compiler-output" == "true"
+    }
+
+    @Test
+    void canHandleCircularModuleDependencies() {
+        def repoDir = file("repo")
+        def artifact1 = publishArtifact(repoDir, "myGroup", "myArtifact1", "myArtifact2")
+        def artifact2 = publishArtifact(repoDir, "myGroup", "myArtifact2", "myArtifact1")
+
+        runIdeaTask """
+apply plugin: "java"
+apply plugin: "idea"
+
+repositories {
+    mavenRepo urls: "${repoDir.toURI()}"
+}
+
+dependencies {
+    compile "myGroup:myArtifact1:1.0"
+}
+        """
+
+        def module = parseImlFile("root")
+        def libs = module.component.orderEntry.library
+        assert libs.size() == 2
+        assert libs.CLASSES.root*. at url*.text().collect { new File(it).name } as Set == [artifact1.name + "!", artifact2.name + "!"] as Set
+    }
+
+    @Test
+    void onlyAddsSourceDirsThatExistOnFileSystem() {
+        runIdeaTask """
+apply plugin: "java"
+apply plugin: "groovy"
+apply plugin: "idea"
+
+sourceSets.main.java.srcDirs.each { it.mkdirs() }
+sourceSets.main.resources.srcDirs.each { it.mkdirs() }
+sourceSets.test.groovy.srcDirs.each { it.mkdirs() }
+        """
+
+        def module = parseImlFile("root")
+        def sourceFolders = module.component.content.sourceFolder
+        def urls = sourceFolders*. at url*.text()
+
+        assert containsDir("src/main/java", urls)
+        assert !containsDir("src/main/groovy", urls)
+        assert containsDir("src/main/resources", urls)
+        assert !containsDir("src/test/java", urls)
+        assert containsDir("src/test/groovy", urls)
+        assert !containsDir("src/test/resources", urls)
+    }
+
+
+    @Test
+    void triggersWithXmlConfigurationHooks() {
+        runIdeaTask '''
+apply plugin: 'java'
+apply plugin: 'idea'
+
+def hookActivated = 0
+
+ideaModule {
+    withXml { hookActivated++ }
+}
+
+idea << {
+    assert hookActivated == 1 : "withXml() hook shoold be fired"
+}
+'''
+    }
+
+    @Test
+    void respectsPerConfigurationExcludes() {
+        def repoDir = file("repo")
+        def artifact1 = publishArtifact(repoDir, "myGroup", "myArtifact1", "myArtifact2")
+        def artifact2 = publishArtifact(repoDir, "myGroup", "myArtifact2")
+
+        runIdeaTask """
+apply plugin: 'java'
+apply plugin: 'idea'
+
+repositories {
+    mavenRepo urls: "${repoDir.toURI()}"
+}
+
+configurations {
+    compile.exclude module: 'myArtifact2'
+}
+
+dependencies {
+    compile "myGroup:myArtifact1:1.0"
+}
+        """
+
+        def module = parseImlFile("root")
+        def libs = module.component.orderEntry.library
+        assert libs.size() == 1
+    }
+
+    @Test
+    void respectsPerDependencyExcludes() {
+        def repoDir = file("repo")
+        def artifact1 = publishArtifact(repoDir, "myGroup", "myArtifact1", "myArtifact2")
+        def artifact2 = publishArtifact(repoDir, "myGroup", "myArtifact2")
+
+        runIdeaTask """
+apply plugin: 'java'
+apply plugin: 'idea'
+
+repositories {
+    mavenRepo urls: "${repoDir.toURI()}"
+}
+
+dependencies {
+    compile("myGroup:myArtifact1:1.0") {
+        exclude module: "myArtifact2"
+    }
+}
+        """
+
+        def module = parseImlFile("root")
+        def libs = module.component.orderEntry.library
+        assert libs.size() == 1
+    }
+
+    private void assertHasExpectedContents(String path) {
+        TestFile file = testDir.file(path).assertIsFile()
+        TestFile expectedFile = testDir.file("expectedFiles/${path}.xml").assertIsFile()
+
+        def cache = distribution.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())
+        try {
+            XMLAssert.assertXMLEqual(diff, true)
+        } catch (AssertionFailedError e) {
+            throw new AssertionFailedError("generated file '$path' does not contain the expected contents: ${e.message}.\nExpected:\n${expectedXml}\nActual:\n${file.text}").initCause(e)
+        }
+    }
+
+    private containsDir(path, urls) {
+        urls.any { it.endsWith(path) }
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaModuleIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaModuleIntegrationTest.groovy
new file mode 100644
index 0000000..a975411
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaModuleIntegrationTest.groovy
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.idea
+
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.plugins.ide.AbstractIdeIntegrationTest
+import org.junit.Rule
+import org.junit.Test
+
+class IdeaModuleIntegrationTest extends AbstractIdeIntegrationTest {
+    @Rule
+    public final TestResources testResources = new TestResources()
+
+    @Test
+    void enablesCustomizationsOnNewModel() {
+        //given
+        testResources.dir.create {
+            additionalCustomSources {}
+            additionalCustomTestSources {}
+            muchBetterOutputDir {}
+            muchBetterTestOutputDir {}
+            customImlFolder {}
+            excludeMePlease {}
+            customModuleContentRoot {}
+            src { main { java {} } }
+        }
+
+        //when
+        runTask 'idea', '''
+apply plugin: "java"
+apply plugin: "idea"
+
+configurations {
+  provided
+  provided.extendsFrom(compile)
+}
+
+idea {
+    module {
+        name = 'foo'
+        contentRoot = file('customModuleContentRoot')
+
+        sourceDirs += file('additionalCustomSources')
+        testSourceDirs += file('additionalCustomTestSources')
+        excludeDirs += file('excludeMePlease')
+
+        scopes.PROVIDED.plus += configurations.compile
+        downloadJavadoc = true
+        downloadSources = false
+
+        inheritOutputDirs = false
+        outputDir = file('muchBetterOutputDir')
+        testOutputDir = file('muchBetterTestOutputDir')
+
+        javaVersion = '1.6'
+        variables = [CUSTOM_VARIABLE: file('customModuleContentRoot').parentFile]
+
+        iml {
+            generateTo = file('customImlFolder')
+
+            withXml {
+                def node = it.asNode()
+                node.appendNode('someInterestingConfiguration', 'hey!')
+            }
+        }
+    }
+}
+'''
+
+        //then
+        def iml = parseImlFile('customImlFolder/foo')
+        ['additionalCustomSources', 'additionalCustomTestSources', 'src/main/java'].each { expectedSrcFolder ->
+            assert iml.component.content.sourceFolder.find { it. at url.text().contains(expectedSrcFolder) }
+        }
+        ['customModuleContentRoot', 'CUSTOM_VARIABLE'].each {
+            assert iml.component.content. at url.text().contains(it)
+        }
+        ['.gradle', 'build', 'excludeMePlease'].each { expectedExclusion ->
+            assert iml.component.content.excludeFolder.find { it. at url.text().endsWith(expectedExclusion) }
+        }
+        assert iml.component.output. at url.text().endsWith('muchBetterOutputDir')
+        assert iml.component."output-test". at url.text().endsWith('muchBetterTestOutputDir')
+        assert iml.component.orderEntry.any { it. at type.text() == 'jdk' && it. at jdkName.text() == '1.6' }
+        assert iml.someInterestingConfiguration.text() == 'hey!'
+    }
+
+    @Test
+    void plusMinusConfigurationsAreCorrectlyApplied() {
+        file('foo.jar', 'bar.jar')
+        //when
+        runTask 'idea', '''
+apply plugin: "java"
+apply plugin: "idea"
+
+configurations {
+  bar
+  foo
+  foo.extendsFrom(bar)
+}
+
+dependencies {
+  bar files('bar.jar')
+  foo files('foo.jar')
+}
+
+idea {
+    module {
+        scopes.COMPILE.plus += configurations.foo
+        scopes.COMPILE.minus += configurations.bar
+    }
+}
+'''
+        def content = getFile([:], 'root.iml').text
+
+        //then
+        assert content.contains('foo.jar')
+        assert !content.contains('bar.jar')
+    }
+
+    @Test
+    void allowsReconfiguringBeforeOrAfterMerging() {
+        //given
+        def existingIml = file('root.iml')
+        existingIml << '''<?xml version="1.0" encoding="UTF-8"?>
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output/>
+    <orderEntry type="inheritedJdk"/>
+    <content url="file://$MODULE_DIR$/">
+      <excludeFolder url="file://$MODULE_DIR$/folderThatWasExcludedEarlier"/>
+    </content>
+    <orderEntry type="sourceFolder" forTests="false"/>
+  </component>
+  <component name="ModuleRootManager"/>
+</module>'''
+
+        //when
+        runTask(['idea'], '''
+apply plugin: "java"
+apply plugin: "idea"
+
+idea {
+    module {
+        excludeDirs = [project.file('folderThatIsExcludedNow')] as Set
+        iml {
+            beforeMerged { it.excludeFolders.clear() }
+            whenMerged   { it.javaVersion = '1.33'   }
+        }
+    }
+}
+''')
+        //then
+        def iml = getFile([:], 'root.iml').text
+        assert iml.contains('folderThatIsExcludedNow')
+        assert !iml.contains('folderThatWasExcludedEarlier')
+        assert iml.contains('1.33')
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaMultiModuleIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaMultiModuleIntegrationTest.groovy
new file mode 100644
index 0000000..50776d8
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaMultiModuleIntegrationTest.groovy
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.idea
+
+import org.gradle.integtests.fixtures.TestResources
+import org.junit.Rule
+import org.junit.Test
+import org.gradle.plugins.ide.AbstractIdeIntegrationTest
+
+/**
+ * @author Szczepan Faber, @date 03.03.11
+ */
+class IdeaMultiModuleIntegrationTest extends AbstractIdeIntegrationTest {
+    @Rule
+    public final TestResources testResources = new TestResources()
+
+    @Test
+    void buildsCorrectModuleDependencies() {
+        def settingsFile = file("master/settings.gradle")
+        settingsFile << """
+include 'api'
+include 'shared:api', 'shared:model'
+include 'util'
+        """
+
+        def buildFile = file("master/build.gradle")
+        buildFile << """
+allprojects {
+    apply plugin: 'java'
+    apply plugin: 'idea'
+}
+
+project(':api') {
+    dependencies {
+        compile project(':shared:api')
+        testCompile project(':shared:model')
+    }
+}
+
+project(':shared:model') {
+    configurations {
+        utilities { extendsFrom testCompile }
+    }
+    dependencies {
+        utilities project(':util')
+    }
+    ideaModule {
+        scopes.TEST.plus.add(configurations.utilities)
+    }
+}
+"""
+
+        //when
+        executer.usingBuildScript(buildFile).usingSettingsFile(settingsFile).withTasks("ideaModule").run()
+
+        //then
+        def apiDeps = parseImlDependencies(project: 'master/api', "api.iml")
+        ['shared-api', 'model'].each { assert apiDeps.contains(it) }
+        def modelDeps = parseImlDependencies(project: 'master/shared/model', "model.iml")
+        ['util'].each { assert modelDeps.contains(it) }
+    }
+
+    @Test
+    void dealsWithDuplicatedModuleNames() {
+      /*
+      This is the multi-module project structure the integration test works with:
+      -root
+        -api
+        -shared
+          -api
+          -model
+        -services
+          -util
+        -util
+        -contrib
+          -services
+            -utilities (renamed by user to 'util'
+      */
+
+        def settingsFile = file("master/settings.gradle")
+        settingsFile << """
+include 'api'
+include 'shared:api', 'shared:model'
+include 'services:utilities'
+include 'util'
+include 'contrib:services:util'
+        """
+
+        def buildFile = file("master/build.gradle")
+        buildFile << """
+allprojects {
+    apply plugin: 'java'
+    apply plugin: 'idea'
+}
+
+project(':api') {
+    dependencies {
+        compile project(':shared:api'), project(':shared:model')
+    }
+}
+
+project(':shared:model') {
+    ideaModule {
+        moduleName = 'very-cool-model'
+    }
+}
+
+project(':services:utilities') {
+    dependencies {
+        compile project(':util'), project(':contrib:services:util'), project(':shared:api'), project(':shared:model')
+    }
+    ideaModule {
+        moduleName = 'util'
+    }
+}
+"""
+
+        //when
+        executer.usingBuildScript(buildFile).usingSettingsFile(settingsFile).withTasks("idea").run()
+//        println(getTestDir())
+
+        //then
+        assertIprContainsCorrectModules()
+        assertApiModuleContainsCorrectDependencies()
+        assertServicesUtilModuleContainsCorrectDependencies()
+    }
+
+    def assertServicesUtilModuleContainsCorrectDependencies() {
+        List moduleDeps = parseImlDependencies(project: 'master/services/utilities', "services-util.iml")
+
+        assert moduleDeps.contains("very-cool-model")
+        assert moduleDeps.contains("util")
+        assert moduleDeps.contains("shared-api")
+        assert moduleDeps.contains("contrib-services-util")
+    }
+
+    List parseImlDependencies(options, file) {
+        def iml = parseFile(options, file)
+        def moduleDeps = iml.component.orderEntry.'@module-name'.collect { it.text() }
+        return moduleDeps
+    }
+
+    def assertApiModuleContainsCorrectDependencies() {
+        def moduleDeps = parseImlDependencies(project: 'master/api', "api.iml")
+
+        assert moduleDeps.contains("very-cool-model")
+        assert moduleDeps.contains("shared-api")
+    }
+
+    def assertIprContainsCorrectModules() {
+        List moduleFileNames = parseIprModules()
+
+        ['master.iml',
+         'shared-api.iml', 'shared.iml',
+         'services.iml', 'services-util.iml',
+         'contrib-services-util.iml', 'contrib.iml', 'contrib-services.iml',
+         'very-cool-model.iml',
+         'api.iml',
+         'util.iml'].each {
+            assert moduleFileNames.contains(it)
+        }
+    }
+
+    List parseIprModules() {
+        def ipr = parseFile(project: 'master', "master.ipr")
+        ipr.component.modules.module. at filepath.collect {
+            it.text().replaceAll(/.*\//, "")
+        }
+    }
+
+    @Test
+    void allowsFullyReconfiguredModuleNames() {
+        //use case from the mailing list
+        def settingsFile = file("master/settings.gradle")
+        settingsFile << "include 'api', 'shared:model'"
+
+        def buildFile = file("master/build.gradle")
+        buildFile << """
+allprojects {
+    apply plugin: 'java'
+    apply plugin: 'idea'
+}
+
+subprojects {
+    ideaModule {
+        outputFile = file(project.projectDir.canonicalPath + "/" + rootProject.name + project.path.replace(':', '.') + ".iml")
+    }
+}
+
+project(':api') {
+    dependencies {
+        compile project(':shared:model')
+    }
+}
+"""
+
+        //when
+        executer.usingBuildScript(buildFile).usingSettingsFile(settingsFile).withTasks("idea").run()
+
+        //then
+        def moduleFileNames = parseIprModules()
+
+        assert moduleFileNames.contains("master.shared.model.iml")
+        assert moduleFileNames.contains("master.api.iml")
+        assert moduleFileNames.contains("master.shared.iml")
+        assert moduleFileNames.contains("master.iml")
+    }
+
+    @Test
+    void cleansCorrectlyWhenModuleNamesAreChangedOrDeduplicated() {
+        def settingsFile = file("master/settings.gradle")
+        settingsFile << "include 'api', 'shared:api', 'contrib'"
+
+        def buildFile = file("master/build.gradle")
+        buildFile << """
+allprojects {
+    apply plugin: 'java'
+    apply plugin: 'idea'
+}
+
+project(':contrib') {
+    ideaModule {
+        moduleName = 'cool-contrib'
+    }
+}
+"""
+
+        executer.usingBuildScript(buildFile).usingSettingsFile(settingsFile).withTasks("idea").run()
+        assert getFile(project: 'master/shared/api', "shared-api.iml").exists()
+        assert getFile(project: 'master/contrib', "cool-contrib.iml").exists()
+
+        //when
+        executer.usingBuildScript(buildFile).usingSettingsFile(settingsFile).withTasks("cleanIdea").run()
+
+        //then
+        assert !getFile(project: 'master/shared/api', "shared-api.iml").exists()
+        assert !getFile(project: 'master/contrib', "cool-contrib.iml").exists()
+    }
+
+    @Test
+    void handlesInternalDependenciesToNonIdeaProjects() {
+        def settingsFile = file("master/settings.gradle")
+        settingsFile << "include 'api', 'nonIdeaProject'"
+
+        def buildFile = file("master/build.gradle")
+        buildFile << """
+subprojects {
+  apply plugin: 'java'
+}
+
+project(':api') {
+    apply plugin: 'idea'
+
+    dependencies {
+        compile project(':nonIdeaProject')
+    }
+}
+"""
+
+        //when
+        executer.usingBuildScript(buildFile).usingSettingsFile(settingsFile).withTasks("idea").run()
+
+        //then
+        assert getFile(project: 'master/api', 'api.iml').exists()
+    }
+
+    @Test
+    void doesNotCreateDuplicateEntriesInIpr() {
+        def settingsFile = file("master/settings.gradle")
+        settingsFile << "include 'api', 'iml'"
+
+        def buildFile = file("master/build.gradle")
+        buildFile << """
+allprojects {
+    apply plugin: 'java'
+    apply plugin: 'idea'
+}
+"""
+
+        //when
+        2.times { executer.usingBuildScript(buildFile).usingSettingsFile(settingsFile).withTasks("ideaProject").run() }
+
+        //then
+        String content = getFile(project: 'master', 'master.ipr').text
+        assert content.count('filepath="$PROJECT_DIR$/api/api.iml"') == 1
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaProjectIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaProjectIntegrationTest.groovy
new file mode 100644
index 0000000..61e7b57
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaProjectIntegrationTest.groovy
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.idea
+
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.plugins.ide.AbstractIdeIntegrationTest
+import org.junit.Rule
+import org.junit.Test
+
+class IdeaProjectIntegrationTest extends AbstractIdeIntegrationTest {
+    @Rule
+    public final TestResources testResources = new TestResources()
+
+    @Test
+    void enablesCustomizationsOnNewModel() {
+        //when
+        runTask 'idea', 'include "someProjectThatWillBeExcluded", "api"', '''
+allprojects {
+    apply plugin: "java"
+    apply plugin: "idea"
+}
+
+idea {
+    project {
+        javaVersion = '1.44'
+        wildcards += '!?*.ruby'
+
+        //let's remove one of the subprojects from generation:
+        subprojects -= project(':someProjectThatWillBeExcluded')
+
+        outputFile = new File(outputFile.parentFile, 'someBetterName.ipr')
+
+        ipr {
+            withXml {
+                def node = it.asNode()
+                node.appendNode('someInterestingConfiguration', 'hey buddy!')
+            }
+        }
+    }
+}
+'''
+
+        //then
+        def ipr = getFile([:], 'someBetterName.ipr').text
+        assert ipr.contains('1.44')
+        assert ipr.contains('!?*.ruby')
+        assert !ipr.contains('someProjectThatWillBeExcluded')
+        assert ipr.contains('hey buddy!')
+    }
+}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/build.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/build.gradle
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/build.gradle
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/integTest/java/org/gradle/SomeClass.java b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/integTest/java/org/gradle/SomeClass.java
new file mode 100644
index 0000000..3495b1d
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/integTest/java/org/gradle/SomeClass.java
@@ -0,0 +1,3 @@
+package org.gradle;
+
+public class SomeClass {}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/main/java/org/gradle/api/PersonList.java b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/main/java/org/gradle/api/PersonList.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/main/java/org/gradle/api/PersonList.java
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/main/java/org/gradle/api/PersonList.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/main/resources/someprops.properties b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/main/resources/someprops.properties
similarity index 100%
copy from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/main/resources/someprops.properties
copy to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/main/resources/someprops.properties
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/test/java/org/gradle/shared/PersonTest.java b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/test/java/org/gradle/shared/PersonTest.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/test/java/org/gradle/shared/PersonTest.java
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/test/java/org/gradle/shared/PersonTest.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/main/resources/someprops.properties b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/test/resources/someprops.properties
similarity index 100%
copy from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/main/resources/someprops.properties
copy to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/test/resources/someprops.properties
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiClasspath.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiClasspath.xml
new file mode 100644
index 0000000..67610d0
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiClasspath.xml
@@ -0,0 +1,20 @@
+<classpath>
+	<classpathentry kind="output" path="bin"/>
+	<classpathentry kind="src" path="src/integTest/java"/>
+	<classpathentry kind="src" path="src/main/resources"/>
+	<classpathentry kind="src" path="src/main/java"/>
+	<classpathentry kind="src" path="src/test/resources"/>
+	<classpathentry kind="src" path="src/test/java"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER" exported="true"/>
+	<classpathentry sourcepath="@CACHE_DIR@/commons-collections/commons-collections/sources/commons-collections-3.2-sources.jar" kind="lib"
+					path="@CACHE_DIR@/commons-collections/commons-collections/jars/commons-collections-3.2.jar" exported="true">
+		<attributes>
+			<attribute name="org.eclipse.jst.component.dependency" value="../"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry sourcepath="@CACHE_DIR@/junit/junit/sources/junit-4.7-sources.jar" kind="lib" path="@CACHE_DIR@/junit/junit/jars/junit-4.7.jar" exported="true">
+		<attributes>
+			<attribute name="org.eclipse.jst.component.dependency" value="../"/>
+		</attributes>
+	</classpathentry>
+</classpath>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiJdt.properties b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiJdt.properties
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiJdt.properties
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiJdt.properties
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiProject.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiProject.xml
new file mode 100644
index 0000000..23ed11d
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiProject.xml
@@ -0,0 +1,26 @@
+<projectDescription>
+	<name>api</name>
+	<comment/>
+	<projects/>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
+		<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
+		<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
+	</natures>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments/>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.wst.common.project.facet.core.builder</name>
+			<arguments/>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.wst.validation.validationbuilder</name>
+			<arguments/>
+		</buildCommand>
+	</buildSpec>
+	<linkedResources/>
+</projectDescription>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/groovyprojectClasspath.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/groovyprojectClasspath.xml
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/groovyprojectClasspath.xml
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/groovyprojectClasspath.xml
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/groovyprojectJdt.properties b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/groovyprojectJdt.properties
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/groovyprojectJdt.properties
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/groovyprojectJdt.properties
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/groovyprojectProject.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/groovyprojectProject.xml
new file mode 100644
index 0000000..48fce77
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/groovyprojectProject.xml
@@ -0,0 +1,16 @@
+<projectDescription>
+	<name>groovyproject</name>
+	<comment/>
+	<projects/>
+	<natures>
+		<nature>org.eclipse.jdt.groovy.core.groovyNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments/>
+		</buildCommand>
+	</buildSpec>
+	<linkedResources/>
+</projectDescription>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/javabaseprojectClasspath.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/javabaseprojectClasspath.xml
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/javabaseprojectClasspath.xml
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/javabaseprojectClasspath.xml
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/javabaseprojectJdt.properties b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/javabaseprojectJdt.properties
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/javabaseprojectJdt.properties
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/javabaseprojectJdt.properties
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/javabaseprojectProject.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/javabaseprojectProject.xml
new file mode 100644
index 0000000..cb6a1af
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/javabaseprojectProject.xml
@@ -0,0 +1,15 @@
+<projectDescription>
+	<name>javabaseproject</name>
+	<comment/>
+	<projects/>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments/>
+		</buildCommand>
+	</buildSpec>
+	<linkedResources/>
+</projectDescription>
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/masterProject.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/masterProject.xml
new file mode 100644
index 0000000..29e6e5a
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/masterProject.xml
@@ -0,0 +1,8 @@
+<projectDescription>
+	<name>master</name>
+	<comment/>
+	<projects/>
+	<natures/>
+	<buildSpec/>
+	<linkedResources/>
+</projectDescription>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppJava6Classpath.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppJava6Classpath.xml
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppJava6Classpath.xml
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppJava6Classpath.xml
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppJava6Jdt.properties b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppJava6Jdt.properties
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppJava6Jdt.properties
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppJava6Jdt.properties
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppJava6Project.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppJava6Project.xml
new file mode 100644
index 0000000..92c78b7
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppJava6Project.xml
@@ -0,0 +1,26 @@
+<projectDescription>
+	<name>webAppJava6</name>
+	<comment/>
+	<projects/>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
+		<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
+		<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
+	</natures>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments/>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.wst.common.project.facet.core.builder</name>
+			<arguments/>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.wst.validation.validationbuilder</name>
+			<arguments/>
+		</buildCommand>
+	</buildSpec>
+	<linkedResources/>
+</projectDescription>
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppJava6WtpComponent.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppJava6WtpComponent.xml
new file mode 100644
index 0000000..a093617
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppJava6WtpComponent.xml
@@ -0,0 +1,7 @@
+<project-modules id="moduleCoreId" project-version="2.0">
+	<wb-module deploy-name="webAppJava6">
+		<property name="context-root" value="webAppJava6"/>
+		<wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/java"/>
+		<wb-resource deploy-path="/" source-path="src/main/webapp"/>
+	</wb-module>
+</project-modules>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppJava6WtpFacet.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppJava6WtpFacet.xml
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppJava6WtpFacet.xml
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppJava6WtpFacet.xml
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppWithVarsClasspath.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppWithVarsClasspath.xml
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppWithVarsClasspath.xml
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppWithVarsClasspath.xml
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppWithVarsJdt.properties b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppWithVarsJdt.properties
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppWithVarsJdt.properties
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppWithVarsJdt.properties
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppWithVarsProject.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppWithVarsProject.xml
new file mode 100644
index 0000000..471b1de
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppWithVarsProject.xml
@@ -0,0 +1,26 @@
+<projectDescription>
+	<name>webAppWithVars</name>
+	<comment/>
+	<projects/>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
+		<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
+		<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
+	</natures>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments/>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.wst.common.project.facet.core.builder</name>
+			<arguments/>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.wst.validation.validationbuilder</name>
+			<arguments/>
+		</buildCommand>
+	</buildSpec>
+	<linkedResources/>
+</projectDescription>
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppWithVarsWtpComponent.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppWithVarsWtpComponent.xml
new file mode 100644
index 0000000..5705cec
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppWithVarsWtpComponent.xml
@@ -0,0 +1,10 @@
+<project-modules id="moduleCoreId" project-version="2.0">
+	<wb-module deploy-name="webAppWithVars">
+		<property name="context-root" value="webAppWithVars"/>
+		<wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/java"/>
+		<wb-resource deploy-path="/" source-path="src/main/webapp"/>
+		<dependent-module deploy-path="/WEB-INF/lib" handle="module:/classpath/var/GRADLE_USER_HOME//cache/commons-lang/commons-lang/jars/commons-lang-2.5.jar">
+			<dependency-type>uses</dependency-type>
+		</dependent-module>
+	</wb-module>
+</project-modules>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppWithVarsWtpFacet.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppWithVarsWtpFacet.xml
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppWithVarsWtpFacet.xml
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webAppWithVarsWtpFacet.xml
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webserviceClasspath.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webserviceClasspath.xml
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webserviceClasspath.xml
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webserviceClasspath.xml
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webserviceJdt.properties b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webserviceJdt.properties
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webserviceJdt.properties
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webserviceJdt.properties
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webserviceProject.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webserviceProject.xml
new file mode 100644
index 0000000..244055a
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webserviceProject.xml
@@ -0,0 +1,26 @@
+<projectDescription>
+	<name>webservice</name>
+	<comment/>
+	<projects/>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
+		<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
+		<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
+	</natures>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments/>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.wst.common.project.facet.core.builder</name>
+			<arguments/>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.wst.validation.validationbuilder</name>
+			<arguments/>
+		</buildCommand>
+	</buildSpec>
+	<linkedResources/>
+</projectDescription>
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webserviceWtpComponent.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webserviceWtpComponent.xml
new file mode 100644
index 0000000..ba922f1
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webserviceWtpComponent.xml
@@ -0,0 +1,13 @@
+<project-modules id="moduleCoreId" project-version="2.0">
+	<wb-module deploy-name="webservice">
+		<property name="context-root" value="webservice"/>
+		<wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/java"/>
+		<wb-resource deploy-path="/" source-path="src/main/webapp"/>
+		<dependent-module deploy-path="/WEB-INF/lib" handle="module:/resource/api/api">
+			<dependency-type>uses</dependency-type>
+		</dependent-module>
+		<dependent-module deploy-path="/WEB-INF/lib" handle="module:/classpath/lib/@CACHE_DIR@/commons-lang/commons-lang/jars/commons-lang-2.5.jar">
+			<dependency-type>uses</dependency-type>
+		</dependent-module>
+	</wb-module>
+</project-modules>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webserviceWtpFacet.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webserviceWtpFacet.xml
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webserviceWtpFacet.xml
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webserviceWtpFacet.xml
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/build.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/build.gradle
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/build.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/src/main/groovy/script.groovy b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/src/main/groovy/script.groovy
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/src/main/groovy/script.groovy
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/src/main/groovy/script.groovy
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/src/main/java/org/gradle/api/PersonList.java b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/src/main/java/org/gradle/api/PersonList.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/src/main/java/org/gradle/api/PersonList.java
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/src/main/java/org/gradle/api/PersonList.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/main/resources/someprops.properties b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/src/main/resources/someprops.properties
similarity index 100%
copy from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/main/resources/someprops.properties
copy to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/src/main/resources/someprops.properties
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/src/test/java/org/gradle/shared/PersonTest.java b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/src/test/java/org/gradle/shared/PersonTest.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/src/test/java/org/gradle/shared/PersonTest.java
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/src/test/java/org/gradle/shared/PersonTest.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/main/resources/someprops.properties b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/src/test/resources/someprops.properties
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/api/src/main/resources/someprops.properties
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/groovyproject/src/test/resources/someprops.properties
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/javabaseproject/build.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/javabaseproject/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/javabaseproject/build.gradle
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/javabaseproject/build.gradle
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/master/build.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/master/build.gradle
new file mode 100644
index 0000000..a541c43
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/master/build.gradle
@@ -0,0 +1,107 @@
+import org.custommonkey.xmlunit.Diff
+import org.custommonkey.xmlunit.ElementNameAndAttributeQualifier
+import org.custommonkey.xmlunit.XMLAssert
+import junit.framework.AssertionFailedError
+import org.junit.ComparisonFailure
+
+buildscript {
+    repositories {
+        mavenCentral()
+    }
+    dependencies {
+        classpath 'xmlunit:xmlunit:1.3'
+    }
+}
+
+defaultTasks 'eclipse', 'cleanEclipse'
+
+allprojects {
+    apply plugin: 'eclipse'
+}
+
+subprojects {
+    repositories {
+        mavenCentral()
+    }
+
+    group = 'org.gradle'
+    version = '1.0'
+}
+
+allprojects {
+    afterEvaluate { p ->
+        configure(p) {
+            eclipseProject.doLast {
+                compareXmlWithIgnoringOrder(getExpectedXml(file("$rootDir/../expectedFiles/${project.name}Project.xml")),
+                        file(".project").text)
+            }
+
+            if (p.hasProperty('eclipseClasspath')) {
+                eclipseClasspath {
+                    downloadJavadoc = true
+                    doLast {
+                        compareXmlWithIgnoringOrder(getExpectedXml(file("$rootDir/../expectedFiles/${project.name}Classpath.xml")),
+                                file(".classpath").text)
+                    }
+                }
+            }
+
+            if (p.hasProperty('eclipseJdt')) {
+                eclipseJdt {
+                    doLast {
+                        compareProperties(getExpectedXml(file("$rootDir/../expectedFiles/${project.name}Jdt.properties")),
+                                file(".settings/org.eclipse.jdt.core.prefs").text)
+                    }
+                }
+            }
+
+            if (p.hasProperty('eclipseWtpComponent')) {
+                eclipseWtpComponent {
+                    doLast {
+                        compareXmlWithIgnoringOrder(getExpectedXml(file("$rootDir/../expectedFiles/${project.name}WtpComponent.xml")),
+                                file(".settings/org.eclipse.wst.common.component").text)
+                    }
+                }
+            }
+            if (p.hasProperty('eclipseWtpFacet')) {
+                eclipseWtpFacet {
+                    doLast {
+                        compareXmlWithIgnoringOrder(getExpectedXml(file("$rootDir/../expectedFiles/${project.name}WtpFacet.xml")),
+                                file(".settings/org.eclipse.wst.common.project.facet.core.xml").text)
+                    }
+                }
+            }
+            cleanEclipse.doLast {
+                assert !file(".classpath").exists()
+                assert !file(".project").exists()
+                assert !file('.settings').exists() || file('.settings').listFiles().length == 0
+            }
+        }
+    }
+}
+
+void compareProperties(String expectedProperties, String actualProperties) {
+    Properties expected = new Properties()
+    expected.load(new ByteArrayInputStream(expectedProperties.bytes))
+    Properties actual = new Properties()
+    actual.load(new ByteArrayInputStream(actualProperties.bytes))
+    assert expected == actual
+}
+
+void compareXmlWithIgnoringOrder(String expectedXml, String actualXml) {
+    Diff diff = new Diff(expectedXml, actualXml)
+    diff.overrideElementQualifier(new ElementNameAndAttributeQualifier())
+    try {
+        XMLAssert.assertXMLEqual(diff, true)
+    } catch (AssertionFailedError error) {
+        println "EXPECTED:\n${expectedXml}"
+        println "ACTUAL:\n${actualXml}"
+        throw new ComparisonFailure("Unexpected content for generated file: ${error.message}", expectedXml, actualXml).initCause(error)
+    }
+}
+
+String getExpectedXml(File file) {
+    def cache = new File(gradle.gradleUserHomeDir, "/cache").absolutePath.replace(File.separator, '/')
+    return file.text.replace('@CACHE_DIR@', cache)
+}
+
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/master/settings.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/master/settings.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/master/settings.gradle
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/master/settings.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppJava6/build.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppJava6/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppJava6/build.gradle
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppJava6/build.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppJava6/src/main/java/org/gradle/Person.java b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppJava6/src/main/java/org/gradle/Person.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppJava6/src/main/java/org/gradle/Person.java
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppJava6/src/main/java/org/gradle/Person.java
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppJava6/src/main/webapp/index.html b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppJava6/src/main/webapp/index.html
new file mode 100644
index 0000000..eef8a3e
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppJava6/src/main/webapp/index.html
@@ -0,0 +1 @@
+<p>index page.</p>
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppWithVars/build.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppWithVars/build.gradle
new file mode 100644
index 0000000..9cd7844
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppWithVars/build.gradle
@@ -0,0 +1,9 @@
+apply plugin: 'war'
+
+dependencies {
+    runtime "commons-lang:commons-lang:2.5"
+    testCompile 'junit:junit:4.7'
+}
+
+eclipseClasspath.variables = ['GRADLE_USER_HOME': gradle.gradleUserHomeDir]
+eclipseWtpComponent.variables = ['GRADLE_USER_HOME': gradle.gradleUserHomeDir]
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppWithVars/src/main/java/org/gradle/Person.java b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppWithVars/src/main/java/org/gradle/Person.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppWithVars/src/main/java/org/gradle/Person.java
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppWithVars/src/main/java/org/gradle/Person.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/webservice/build.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/webservice/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/webservice/build.gradle
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/webservice/build.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/webservice/src/main/java/org/gradle/webservice/TestTest.java b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/webservice/src/main/java/org/gradle/webservice/TestTest.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/EclipseIntegrationTest/canCreateAndDeleteMetaData/webservice/src/main/java/org/gradle/webservice/TestTest.java
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/webservice/src/main/java/org/gradle/webservice/TestTest.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/api/build.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/api/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/api/build.gradle
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/api/build.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/api/src/main/java/org/gradle/api/PersonList.java b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/api/src/main/java/org/gradle/api/PersonList.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/api/src/main/java/org/gradle/api/PersonList.java
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/api/src/main/java/org/gradle/api/PersonList.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/api/src/test/java/org/gradle/shared/PersonTest.java b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/api/src/test/java/org/gradle/shared/PersonTest.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/api/src/test/java/org/gradle/shared/PersonTest.java
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/api/src/test/java/org/gradle/shared/PersonTest.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/build.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/build.gradle
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/build.gradle
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/api/api.iml.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/api/api.iml.xml
new file mode 100644
index 0000000..6ea0ae7
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/api/api.iml.xml
@@ -0,0 +1,36 @@
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <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/test/java" isTestSource="true"/>
+      <excludeFolder url="file://$MODULE_DIR$/.gradle"/>
+      <excludeFolder url="file://$MODULE_DIR$/build"/>
+    </content>
+    <orderEntry type="sourceFolder" forTests="false"/>
+    <orderEntry type="module-library" exported="" scope="RUNTIME">
+      <library>
+        <CLASSES>
+          <root url="jar://@CACHE_DIR@/commons-collections/commons-collections/jars/commons-collections-3.2.jar!/"/>
+        </CLASSES>
+        <JAVADOC/>
+        <SOURCES>
+          <root url="jar://@CACHE_DIR@/commons-collections/commons-collections/sources/commons-collections-3.2-sources.jar!/"/>
+        </SOURCES>
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library" scope="TEST">
+      <library>
+        <CLASSES>
+          <root url="jar://@CACHE_DIR@/junit/junit/jars/junit-4.7.jar!/"/>
+        </CLASSES>
+        <JAVADOC/>
+        <SOURCES>
+          <root url="jar://@CACHE_DIR@/junit/junit/sources/junit-4.7-sources.jar!/"/>
+        </SOURCES>
+      </library>
+    </orderEntry>
+  </component>
+  <component name="ModuleRootManager"/>
+</module>
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/root.iml.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/root.iml.xml
new file mode 100644
index 0000000..6d5d57a
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/root.iml.xml
@@ -0,0 +1,12 @@
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output/>
+    <orderEntry type="inheritedJdk"/>
+    <content url="file://$MODULE_DIR$/">
+      <excludeFolder url="file://$MODULE_DIR$/.gradle"/>
+      <excludeFolder url="file://$MODULE_DIR$/build"/>
+    </content>
+    <orderEntry type="sourceFolder" forTests="false"/>
+  </component>
+  <component name="ModuleRootManager"/>
+</module>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/root.ipr.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/root.ipr.xml
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/root.ipr.xml
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/root.ipr.xml
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/root.iws.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/root.iws.xml
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/root.iws.xml
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/root.iws.xml
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webservice/webservice.iml.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webservice/webservice.iml.xml
new file mode 100644
index 0000000..647bac0
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webservice/webservice.iml.xml
@@ -0,0 +1,71 @@
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output/>
+    <orderEntry type="inheritedJdk"/>
+    <content url="file://$MODULE_DIR$/">
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false"/>
+      <excludeFolder url="file://$MODULE_DIR$/.gradle"/>
+      <excludeFolder url="file://$MODULE_DIR$/build"/>
+    </content>
+    <orderEntry type="sourceFolder" forTests="false"/>
+    <orderEntry type="module-library" exported="">
+      <library>
+        <CLASSES>
+          <root url="jar://@CACHE_DIR@/org.slf4j/slf4j-api/jars/slf4j-api-1.5.8.jar!/"/>
+        </CLASSES>
+        <JAVADOC/>
+        <SOURCES>
+          <root url="jar://@CACHE_DIR@/org.slf4j/slf4j-api/sources/slf4j-api-1.5.8-sources.jar!/"/>
+        </SOURCES>
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library" exported="">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/compile-1.0.jar!/"/>
+        </CLASSES>
+        <JAVADOC/>
+        <SOURCES/>
+      </library>
+    </orderEntry>
+    <orderEntry type="module" module-name="api" exported=""/>
+    <orderEntry type="module-library" exported="" scope="RUNTIME">
+      <library>
+        <CLASSES>
+          <root url="jar://@CACHE_DIR@/commons-lang/commons-lang/jars/commons-lang-2.4.jar!/"/>
+        </CLASSES>
+        <JAVADOC>
+          <root url="jar://@CACHE_DIR@/commons-lang/commons-lang/javadocs/commons-lang-2.4-javadoc.jar!/"/>
+        </JAVADOC>
+        <SOURCES>
+          <root url="jar://@CACHE_DIR@/commons-lang/commons-lang/sources/commons-lang-2.4-sources.jar!/"/>
+        </SOURCES>
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library" exported="" scope="RUNTIME">
+      <library>
+        <CLASSES>
+          <root url="jar://@CACHE_DIR@/commons-io/commons-io/jars/commons-io-1.2.jar!/"/>
+        </CLASSES>
+        <JAVADOC>
+          <root url="jar://@CACHE_DIR@/commons-io/commons-io/javadocs/commons-io-1.2-javadoc.jar!/"/>
+        </JAVADOC>
+        <SOURCES>
+          <root url="jar://@CACHE_DIR@/commons-io/commons-io/sources/commons-io-1.2-sources.jar!/"/>
+        </SOURCES>
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library" scope="TEST">
+      <library>
+        <CLASSES>
+          <root url="jar://@CACHE_DIR@/junit/junit/jars/junit-4.7.jar!/"/>
+        </CLASSES>
+        <JAVADOC/>
+        <SOURCES>
+          <root url="jar://@CACHE_DIR@/junit/junit/sources/junit-4.7-sources.jar!/"/>
+        </SOURCES>
+      </library>
+    </orderEntry>
+  </component>
+  <component name="ModuleRootManager"/>
+</module>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/settings.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/settings.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/settings.gradle
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/settings.gradle
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/webservice/build.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/webservice/build.gradle
new file mode 100644
index 0000000..255aecb
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/webservice/build.gradle
@@ -0,0 +1,15 @@
+apply plugin: 'war'
+
+version = '2.5'
+
+dependencies {
+    providedCompile 'org.slf4j:slf4j-api:1.5.8 at jar'
+    compile project(':api'), files("$projectDir/lib/compile-1.0.jar")
+    runtime module("commons-lang:commons-lang:2.4") {
+        dependency("commons-io:commons-io:1.2")
+    }
+}
+
+cleanIdea.doLast {
+    assert !file("webservice/webservice.iml").isFile()
+}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/webservice/src/main/java/org/gradle/webservice/TestTest.java b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/webservice/src/main/java/org/gradle/webservice/TestTest.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/webservice/src/main/java/org/gradle/webservice/TestTest.java
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/webservice/src/main/java/org/gradle/webservice/TestTest.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/overwritesExistingDependencies/build.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/overwritesExistingDependencies/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/overwritesExistingDependencies/build.gradle
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/overwritesExistingDependencies/build.gradle
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/overwritesExistingDependencies/expectedFiles/root.iml.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/overwritesExistingDependencies/expectedFiles/root.iml.xml
new file mode 100644
index 0000000..2382905
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/overwritesExistingDependencies/expectedFiles/root.iml.xml
@@ -0,0 +1,33 @@
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output/>
+    <content url="file://$MODULE_DIR$/">
+      <excludeFolder url="file://$MODULE_DIR$/.gradle"/>
+      <excludeFolder url="file://$MODULE_DIR$/build"/>
+    </content>
+    <orderEntry type="inheritedJdk"/>
+    <orderEntry type="sourceFolder" forTests="false"/>
+    <orderEntry type="module-library" exported="" scope="RUNTIME">
+      <library>
+        <CLASSES>
+          <root url="jar://@CACHE_DIR@/commons-collections/commons-collections/jars/commons-collections-3.2.jar!/"/>
+        </CLASSES>
+        <JAVADOC/>
+        <SOURCES>
+          <root url="jar://@CACHE_DIR@/commons-collections/commons-collections/sources/commons-collections-3.2-sources.jar!/"/>
+        </SOURCES>
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library" exported="" scope="RUNTIME">
+      <library>
+        <CLASSES>
+          <root url="jar://@CACHE_DIR@/junit/junit/jars/junit-4.7.jar!/"/>
+        </CLASSES>
+        <JAVADOC/>
+        <SOURCES>
+          <root url="jar://@CACHE_DIR@/junit/junit/sources/junit-4.7-sources.jar!/"/>
+        </SOURCES>
+      </library>
+    </orderEntry>
+  </component>
+</module>
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/overwritesExistingDependencies/root.iml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/overwritesExistingDependencies/root.iml
new file mode 100644
index 0000000..314a2af
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/overwritesExistingDependencies/root.iml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module-library" scope="RUNTIME">
+      <library>
+        <CLASSES>
+          <root url="jar://$CUSTOM_DIR$/../cache/junit/junit/jars/junit-4.7.jar!/"/>
+        </CLASSES>
+        <JAVADOC/>
+        <SOURCES>
+          <root url="jar://$CUSTOM_DIR$/../cache/junit/junit/sources/junit-4.7-sources.jar!/"/>
+        </SOURCES>
+      </library>
+    </orderEntry>
+  </component>
+</module>
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/overwritesExistingDependencies/settings.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/overwritesExistingDependencies/settings.gradle
new file mode 100644
index 0000000..6d3392f
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/overwritesExistingDependencies/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'root'
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/build.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/build.gradle
new file mode 100644
index 0000000..03ed6d5
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/build.gradle
@@ -0,0 +1,4 @@
+apply plugin: 'idea'
+project('a') {
+    apply plugin: 'idea'
+}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/expectedFiles/root.ipr.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/expectedFiles/root.ipr.xml
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/expectedFiles/root.ipr.xml
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/expectedFiles/root.ipr.xml
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/settings.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/settings.gradle
new file mode 100644
index 0000000..5f1dc16
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/settings.gradle
@@ -0,0 +1,3 @@
+include 'a', 'b'
+
+rootProject.name = 'root'
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithAnEmptyProject/build.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithAnEmptyProject/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithAnEmptyProject/build.gradle
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithAnEmptyProject/build.gradle
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithAnEmptyProject/expectedFiles/root.iml.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithAnEmptyProject/expectedFiles/root.iml.xml
new file mode 100644
index 0000000..6d5d57a
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithAnEmptyProject/expectedFiles/root.iml.xml
@@ -0,0 +1,12 @@
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output/>
+    <orderEntry type="inheritedJdk"/>
+    <content url="file://$MODULE_DIR$/">
+      <excludeFolder url="file://$MODULE_DIR$/.gradle"/>
+      <excludeFolder url="file://$MODULE_DIR$/build"/>
+    </content>
+    <orderEntry type="sourceFolder" forTests="false"/>
+  </component>
+  <component name="ModuleRootManager"/>
+</module>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithAnEmptyProject/expectedFiles/root.ipr.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithAnEmptyProject/expectedFiles/root.ipr.xml
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithAnEmptyProject/expectedFiles/root.ipr.xml
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithAnEmptyProject/expectedFiles/root.ipr.xml
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithAnEmptyProject/settings.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithAnEmptyProject/settings.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithAnEmptyProject/settings.gradle
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithAnEmptyProject/settings.gradle
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/root/root.iml.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/root/root.iml.xml
new file mode 100644
index 0000000..6d5d57a
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/root/root.iml.xml
@@ -0,0 +1,12 @@
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output/>
+    <orderEntry type="inheritedJdk"/>
+    <content url="file://$MODULE_DIR$/">
+      <excludeFolder url="file://$MODULE_DIR$/.gradle"/>
+      <excludeFolder url="file://$MODULE_DIR$/build"/>
+    </content>
+    <orderEntry type="sourceFolder" forTests="false"/>
+  </component>
+  <component name="ModuleRootManager"/>
+</module>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/root/root.ipr.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/root/root.ipr.xml
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/root/root.ipr.xml
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/root/root.ipr.xml
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/top-level.iml.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/top-level.iml.xml
new file mode 100644
index 0000000..6d5d57a
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/top-level.iml.xml
@@ -0,0 +1,12 @@
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output/>
+    <orderEntry type="inheritedJdk"/>
+    <content url="file://$MODULE_DIR$/">
+      <excludeFolder url="file://$MODULE_DIR$/.gradle"/>
+      <excludeFolder url="file://$MODULE_DIR$/build"/>
+    </content>
+    <orderEntry type="sourceFolder" forTests="false"/>
+  </component>
+  <component name="ModuleRootManager"/>
+</module>
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/root/build.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithNonStandardLayout/root/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/root/build.gradle
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithNonStandardLayout/root/build.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/settings.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithNonStandardLayout/settings.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/settings.gradle
rename to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithNonStandardLayout/settings.gradle
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/api/GeneratorTask.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/api/GeneratorTask.java
new file mode 100644
index 0000000..b642ead
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/api/GeneratorTask.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.api;
+
+import groovy.lang.Closure;
+import org.gradle.api.Action;
+import org.gradle.api.internal.ConventionTask;
+import org.gradle.api.specs.Specs;
+import org.gradle.api.tasks.OutputFile;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.listener.ActionBroadcast;
+import org.gradle.plugins.ide.internal.generator.generator.Generator;
+
+import java.io.File;
+
+/**
+ * <p>A {@code GeneratorTask} generates a configuration file based on a domain object of type T.
+ * When executed the task:
+ * <ul>
+ *
+ * <li>loads the object from the input file, if it exists.</li>
+ *
+ * <li>Calls the beforeConfigured actions, passing the object to each action.</li>
+ *
+ * <li>Configures the object in some task-specific way.</li>
+ *
+ * <li>Calls the afterConfigured actions, passing the object to each action.</li>
+ *
+ * <li>writes the object to the output file.</li>
+ *
+ * </ul>
+ *
+ * @param <T> The domain object for the configuration file.
+ */
+public class GeneratorTask<T> extends ConventionTask {
+    private File inputFile;
+    private File outputFile;
+    protected final ActionBroadcast<T> beforeConfigured = new ActionBroadcast<T>();
+    protected final ActionBroadcast<T> afterConfigured = new ActionBroadcast<T>();
+    protected Generator<T> generator;
+
+    protected T domainObject;
+
+    public GeneratorTask() {
+        getOutputs().upToDateWhen(Specs.satisfyNone());
+    }
+
+    @TaskAction
+    void generate() {
+        if (getInputFile().exists()) {
+            domainObject = generator.read(getInputFile());
+        } else {
+            domainObject = generator.defaultInstance();
+        }
+        beforeConfigured.execute(domainObject);
+        generator.configure(domainObject);
+        afterConfigured.execute(domainObject);
+
+        generator.write(domainObject, getOutputFile());
+    }
+
+    /**
+     * The input file to load the initial configuration from. Defaults to the output file. If the specified input file
+     * does not exist, this task uses some default initial configuration.
+     *
+     * @return The input file.
+     */
+    public File getInputFile() {
+        return inputFile != null ? inputFile : getOutputFile();
+    }
+
+    /**
+     * Sets the input file to load the initial configuration from.
+     *
+     * @param inputFile The input file. Use null to use the output file.
+     */
+    public void setInputFile(File inputFile) {
+        this.inputFile = inputFile;
+    }
+
+    /**
+     * The output file to write the final configuration to.
+     *
+     * @return The output file.
+     */
+    @OutputFile
+    public File getOutputFile() {
+        return outputFile;
+    }
+
+    /**
+     * Sets the output file to write the final configuration to.
+     *
+     * @param outputFile The output file.
+     */
+    public void setOutputFile(File outputFile) {
+        this.outputFile = outputFile;
+    }
+
+    /**
+     * <p>Adds a closure to be called before the domain object is configured by this task. The domain object is passed
+     * as a parameter to the closure.</p>
+     *
+     * <p>The closure is executed after the domain object has been loaded from the input file. Using this method allows
+     * you to change the domain object in some way before the task configures it.</p>
+     *
+     * @param closure The closure to execute.
+     */
+    public void beforeConfigured(Closure closure) {
+        beforeConfigured.add(closure);
+    }
+
+    /**
+     * <p>Adds an action to be called before the domain object is configured by this task. The domain object is passed
+     * as a parameter to the action.</p>
+     *
+     * <p>The action is executed after the domain object has been loaded from the input file. Using this method allows
+     * you to change the domain object in some way before the task configures it.</p>
+     *
+     * @param action The action to execute.
+     */
+    public void beforeConfigured(Action<? super T> action) {
+        beforeConfigured.add(action);
+    }
+
+    /**
+     * <p>Adds a closure to be called after the domain object has been configured by this task. The domain object is
+     * passed as a parameter to the closure.</p>
+     *
+     * <p>The closure is executed just before the domain object is written to the output file. Using this method allows
+     * you to override the configuration applied by this task.</p>
+     *
+     * @param closure The closure to execute.
+     */
+    public void whenConfigured(Closure closure) {
+        afterConfigured.add(closure);
+    }
+
+    /**
+     * <p>Adds an action to be called after the domain object has been configured by this task. The domain object is
+     * passed as a parameter to the action.</p>
+     *
+     * <p>The action is executed just before the domain object is written to the output file. Using this method allows
+     * you to override the configuration applied by this task.</p>
+     *
+     * @param action The action to execute.
+     */
+    public void whenConfigured(Action<? super T> action) {
+        afterConfigured.add(action);
+    }
+
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/api/XmlGeneratorTask.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/api/XmlGeneratorTask.java
new file mode 100644
index 0000000..f50fd88
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/api/XmlGeneratorTask.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.api;
+
+import groovy.lang.Closure;
+import org.gradle.api.Action;
+import org.gradle.api.artifacts.maven.XmlProvider;
+import org.gradle.api.internal.XmlTransformer;
+import org.gradle.plugins.ide.internal.generator.generator.PersistableConfigurationObject;
+import org.gradle.plugins.ide.internal.generator.generator.PersistableConfigurationObjectGenerator;
+
+/**
+ * A convenience superclass for those tasks which generate XML configuration files from a domain object of type T.
+ *
+ * @param <T> The domain object type.
+ */
+public abstract class XmlGeneratorTask<T extends PersistableConfigurationObject> extends GeneratorTask<T> {
+    private final XmlTransformer xmlTransformer = new XmlTransformer();
+
+    public XmlGeneratorTask() {
+        generator = new PersistableConfigurationObjectGenerator<T>() {
+            public T create() {
+                return XmlGeneratorTask.this.create();
+            }
+
+            public void configure(T object) {
+                XmlGeneratorTask.this.configure(object);
+            }
+        };
+    }
+
+    protected XmlTransformer getXmlTransformer() {
+        return xmlTransformer;
+    }
+
+    protected abstract void configure(T object);
+
+    protected abstract T create();
+
+    /**
+     * Adds a closure to be called when the XML document 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 before
+     * it is written to the output file.
+     *
+     * @param closure The closure to execute when the XML has been created.
+     */
+    public void withXml(Closure closure) {
+        xmlTransformer.addAction(closure);
+    }
+
+    /**
+     * Adds an action to be called when the XML document 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 before
+     * it is written to the output file.
+     *
+     * @param action The action to execute when the IPR XML has been created.
+     */
+    public void withXml(Action<? super XmlProvider> action) {
+        xmlTransformer.addAction(action);
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/api/package-info.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/api/package-info.java
new file mode 100644
index 0000000..43710f3
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/api/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * General ide plugin api.
+ */
+package org.gradle.plugins.ide.api;
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/EclipsePlugin.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/EclipsePlugin.groovy
new file mode 100644
index 0000000..e02bb04
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/EclipsePlugin.groovy
@@ -0,0 +1,304 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.eclipse
+
+import org.gradle.api.JavaVersion
+import org.gradle.api.Project
+import org.gradle.api.artifacts.ProjectDependency
+import org.gradle.api.internal.ClassGenerator
+import org.gradle.api.plugins.GroovyBasePlugin
+import org.gradle.api.plugins.JavaBasePlugin
+import org.gradle.api.plugins.JavaPlugin
+import org.gradle.api.plugins.WarPlugin
+import org.gradle.api.plugins.scala.ScalaBasePlugin
+import org.gradle.plugins.ide.eclipse.internal.EclipseNameDeduper
+import org.gradle.plugins.ide.internal.IdePlugin
+import org.gradle.plugins.ide.eclipse.model.*
+
+/**
+ * <p>A plugin which generates Eclipse files.</p>
+ *
+ * @author Hans Dockter
+ */
+class EclipsePlugin extends IdePlugin {
+    static final String ECLIPSE_TASK_NAME = "eclipse"
+    static final String CLEAN_ECLIPSE_TASK_NAME = "cleanEclipse"
+    static final String ECLIPSE_PROJECT_TASK_NAME = "eclipseProject"
+    static final String ECLIPSE_WTP_COMPONENT_TASK_NAME = "eclipseWtpComponent"
+    static final String ECLIPSE_WTP_FACET_TASK_NAME = "eclipseWtpFacet"
+    static final String ECLIPSE_CP_TASK_NAME = "eclipseClasspath"
+    static final String ECLIPSE_JDT_TASK_NAME = "eclipseJdt"
+
+    EclipseModel model = new EclipseModel()
+
+    @Override protected String getLifecycleTaskName() {
+        return 'eclipse'
+    }
+
+    @Override protected void onApply(Project project) {
+        lifecycleTask.description = 'Generates all Eclipse files.'
+        cleanTask.description = 'Cleans all Eclipse files.'
+
+        project.convention.plugins.eclipse = model
+
+        configureEclipseProject(project)
+        configureEclipseClasspath(project)
+        configureEclipseJdt(project)
+        configureEclipseWtpComponent(project)
+        configureEclipseWtpFacet(project)
+
+        project.gradle.projectsEvaluated {
+            new EclipseNameDeduper().configure(project)
+        }
+    }
+
+    private void configureEclipseProject(Project project) {
+        maybeAddTask(project, this, ECLIPSE_PROJECT_TASK_NAME, GenerateEclipseProject) {
+            //task properties:
+            description = "Generates the Eclipse project file."
+            inputFile = project.file('.project')
+            outputFile = project.file('.project')
+
+            //model:
+            model.project = services.get(ClassGenerator).newInstance(EclipseProject)
+            projectModel = model.project
+
+            projectModel.name = project.name
+            projectModel.conventionMapping.comment = { project.description }
+
+            project.plugins.withType(JavaBasePlugin) {
+                projectModel.buildCommand "org.eclipse.jdt.core.javabuilder"
+                projectModel.natures "org.eclipse.jdt.core.javanature"
+            }
+
+            project.plugins.withType(GroovyBasePlugin) {
+                projectModel.natures.add(natures.indexOf("org.eclipse.jdt.core.javanature"), "org.eclipse.jdt.groovy.core.groovyNature")
+            }
+
+            project.plugins.withType(ScalaBasePlugin) {
+                projectModel.buildCommands.set(buildCommands.findIndexOf { it.name == "org.eclipse.jdt.core.javabuilder" },
+                        new BuildCommand("ch.epfl.lamp.sdt.core.scalabuilder"))
+                projectModel.natures.add(natures.indexOf("org.eclipse.jdt.core.javanature"), "ch.epfl.lamp.sdt.core.scalanature")
+            }
+
+            project.plugins.withType(WarPlugin) {
+                projectModel.buildCommand 'org.eclipse.wst.common.project.facet.core.builder'
+                projectModel.buildCommand 'org.eclipse.wst.validation.validationbuilder'
+                projectModel.natures 'org.eclipse.wst.common.project.facet.core.nature'
+                projectModel.natures 'org.eclipse.wst.common.modulecore.ModuleCoreNature'
+                projectModel.natures 'org.eclipse.jem.workbench.JavaEMFNature'
+
+                doLaterWithEachDependedUponEclipseProject(project) { Project otherProject ->
+                    configureTask(otherProject, ECLIPSE_PROJECT_TASK_NAME) {
+                        projectModel.buildCommand 'org.eclipse.wst.common.project.facet.core.builder'
+                        projectModel.buildCommand 'org.eclipse.wst.validation.validationbuilder'
+                        projectModel.natures 'org.eclipse.wst.common.project.facet.core.nature'
+                        projectModel.natures 'org.eclipse.wst.common.modulecore.ModuleCoreNature'
+                        projectModel.natures 'org.eclipse.jem.workbench.JavaEMFNature'
+                    }
+                }
+            }
+        }
+    }
+
+    private void configureEclipseClasspath(Project project) {
+        model.classpath = project.services.get(ClassGenerator).newInstance(EclipseClasspath, [project: project])
+        model.classpath.conventionMapping.classesOutputDir = { new File(project.projectDir, 'bin') }
+
+        project.plugins.withType(JavaBasePlugin) {
+            maybeAddTask(project, this, ECLIPSE_CP_TASK_NAME, GenerateEclipseClasspath) {
+                //task properties:
+                description = "Generates the Eclipse classpath file."
+                inputFile = project.file('.classpath')
+                outputFile = project.file('.classpath')
+
+                //model properties:
+                classpath = model.classpath
+
+                classpath.sourceSets = project.sourceSets //TODO SF - should be a convenience property?
+                classpath.containers 'org.eclipse.jdt.launching.JRE_CONTAINER'
+
+                project.plugins.withType(JavaPlugin) {
+                    classpath.plusConfigurations = [project.configurations.testRuntime]
+                }
+
+                project.plugins.withType(WarPlugin) {
+                    doLaterWithEachDependedUponEclipseProject(project) { Project otherProject ->
+                        configureTask(otherProject, ECLIPSE_CP_TASK_NAME) {
+                            whenConfigured { Classpath classpath ->
+                                for (entry in classpath.entries) {
+                                    if (entry instanceof Library) {
+                                        // '../' and '/WEB-INF/lib' both seem to be correct (and equivalent) values here
+                                        entry.entryAttributes['org.eclipse.jst.component.dependency'] = '../'
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private void configureEclipseJdt(Project project) {
+        project.plugins.withType(JavaBasePlugin) {
+            maybeAddTask(project, this, ECLIPSE_JDT_TASK_NAME, GenerateEclipseJdt) {
+                //task properties:
+                description = "Generates the Eclipse JDT settings file."
+                outputFile = project.file('.settings/org.eclipse.jdt.core.prefs')
+                inputFile = project.file('.settings/org.eclipse.jdt.core.prefs')
+                //model properties:
+                model.jdt = services.get(ClassGenerator).newInstance(EclipseJdt)
+                jdt = model.jdt
+                jdt.conventionMapping.sourceCompatibility = { project.sourceCompatibility }
+                jdt.conventionMapping.targetCompatibility = { project.targetCompatibility }
+            }
+        }
+    }
+
+    private void configureEclipseWtpComponent(Project project) {
+        project.plugins.withType(WarPlugin) {
+            maybeAddTask(project, this, ECLIPSE_WTP_COMPONENT_TASK_NAME, GenerateEclipseWtpComponent) {
+                //task properties:
+                description = 'Generates the Eclipse WTP component settings file.'
+                inputFile = project.file('.settings/org.eclipse.wst.common.component')
+                outputFile = project.file('.settings/org.eclipse.wst.common.component')
+
+                //model properties:
+                model.wtp.component = services.get(ClassGenerator).newInstance(EclipseWtpComponent, [project: project])
+                component = model.wtp.component
+
+                component.conventionMapping.sourceDirs = { getMainSourceDirs(project) }
+                component.plusConfigurations = [project.configurations.runtime]
+                component.minusConfigurations = [project.configurations.providedRuntime]
+                component.deployName = project.name
+                component.resource deployPath: '/', sourcePath: project.convention.plugins.war.webAppDirName // TODO: not lazy
+                component.conventionMapping.contextPath = { project.war.baseName }
+            }
+
+            doLaterWithEachDependedUponEclipseProject(project) { Project otherProject ->
+                def eclipsePlugin = otherProject.plugins.getPlugin(EclipsePlugin)
+                // require Java plugin because we need source set 'main'
+                // (in the absence of 'main', it probably makes no sense to write the file)
+                otherProject.plugins.withType(JavaPlugin) {
+                    maybeAddTask(otherProject, eclipsePlugin, ECLIPSE_WTP_COMPONENT_TASK_NAME, GenerateEclipseWtpComponent) {
+                        //task properties:
+                        description = 'Generates the Eclipse WTP component settings file.'
+                        inputFile = otherProject.file('.settings/org.eclipse.wst.common.component')
+                        outputFile = otherProject.file('.settings/org.eclipse.wst.common.component')
+
+                        //model properties:
+                        eclipsePlugin.model.wtp.component = services.get(ClassGenerator).newInstance(EclipseWtpComponent, [project: otherProject])
+                        component = eclipsePlugin.model.wtp.component
+
+                        component.deployName = otherProject.name
+                        component.conventionMapping.resources = {
+                            getMainSourceDirs(otherProject).collect { new WbResource("/", otherProject.relativePath(it)) }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private void configureEclipseWtpFacet(Project project) {
+        project.plugins.withType(WarPlugin) {
+            maybeAddTask(project, this, ECLIPSE_WTP_FACET_TASK_NAME, GenerateEclipseWtpFacet) {
+                //task properties:
+                description = 'Generates the Eclipse WTP facet settings file.'
+                inputFile = project.file('.settings/org.eclipse.wst.common.project.facet.core.xml')
+                outputFile = project.file('.settings/org.eclipse.wst.common.project.facet.core.xml')
+
+                //model properties:
+                model.wtp.facet = services.get(ClassGenerator).newInstance(EclipseWtpFacet)
+                facet = model.wtp.facet
+                facet.conventionMapping.facets = { [new Facet("jst.web", "2.4"), new Facet("jst.java", toJavaFacetVersion(project.sourceCompatibility))] }
+            }
+
+            doLaterWithEachDependedUponEclipseProject(project) { Project otherProject ->
+                def eclipsePlugin = otherProject.plugins.getPlugin(EclipsePlugin)
+                maybeAddTask(otherProject, eclipsePlugin, ECLIPSE_WTP_FACET_TASK_NAME, GenerateEclipseWtpFacet) {
+                    //task properties:
+                    description = 'Generates the Eclipse WTP facet settings file.'
+                    inputFile = otherProject.file('.settings/org.eclipse.wst.common.project.facet.core.xml')
+                    outputFile = otherProject.file('.settings/org.eclipse.wst.common.project.facet.core.xml')
+
+                    //model properties:
+                    eclipsePlugin.model.wtp.facet = services.get(ClassGenerator).newInstance(EclipseWtpFacet)
+                    facet = eclipsePlugin.model.wtp.facet
+
+                    facet.conventionMapping.facets = { [new Facet("jst.utility", "1.0")] }
+                    otherProject.plugins.withType(JavaPlugin) {
+                        facet.conventionMapping.facets = {
+                            [new Facet("jst.utility", "1.0"), new Facet("jst.java",
+                                    toJavaFacetVersion(otherProject.sourceCompatibility))]
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    // TODO: might have to search all class paths of all source sets for project dependendencies, not just runtime configuration
+    private void doLaterWithEachDependedUponEclipseProject(Project project, Closure action) {
+        project.gradle.projectsEvaluated {
+            eachDependedUponEclipseProject(project, action)
+        }
+    }
+
+    private void eachDependedUponEclipseProject(Project project, Closure action) {
+        def runtimeConfig = project.configurations.findByName("runtime")
+        if (runtimeConfig) {
+            def projectDeps = runtimeConfig.getAllDependencies(ProjectDependency)
+            def dependedUponProjects = projectDeps*.dependencyProject
+            for (dependedUponProject in dependedUponProjects) {
+                dependedUponProject.plugins.withType(EclipsePlugin) { action(dependedUponProject) }
+                eachDependedUponEclipseProject(dependedUponProject, action)
+            }
+        }
+    }
+
+    private void withTask(Project project, String taskName, Closure action) {
+        project.tasks.matching { it.name == taskName }.all(action)
+    }
+
+    private void configureTask(Project project, String taskName, Closure action) {
+        withTask(project, taskName) { task ->
+            project.configure(task, action)
+        }
+    }
+
+    private void maybeAddTask(Project project, EclipsePlugin plugin, String taskName, Class taskType, Closure action) {
+        if (project.tasks.findByName(taskName)) { return }
+        def task = project.tasks.add(taskName, taskType)
+        project.configure(task, action)
+        plugin.addWorker(task)
+    }
+
+    private String toJavaFacetVersion(JavaVersion version) {
+        if (version == JavaVersion.VERSION_1_5) {
+            return '5.0'
+        }
+        if (version == JavaVersion.VERSION_1_6) {
+            return '6.0'
+        }
+        return version.toString()
+    }
+
+    private Set<File> getMainSourceDirs(Project project) {
+        project.sourceSets.main.allSource.srcDirs as LinkedHashSet
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseClasspath.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseClasspath.groovy
new file mode 100644
index 0000000..63af317
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseClasspath.groovy
@@ -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.plugins.ide.eclipse
+
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.tasks.SourceSet
+import org.gradle.plugins.ide.api.XmlGeneratorTask
+import org.gradle.plugins.ide.eclipse.model.Classpath
+import org.gradle.plugins.ide.eclipse.model.EclipseClasspath
+
+/**
+ * Generates an Eclipse <code>.classpath</code> file.
+ *
+ * @author Hans Dockter
+ */
+class GenerateEclipseClasspath extends XmlGeneratorTask<Classpath> {
+
+    EclipseClasspath classpath
+
+    GenerateEclipseClasspath() {
+        xmlTransformer.indentation = "\t"
+    }
+
+    @Override protected Classpath create() {
+        return new Classpath(xmlTransformer)
+    }
+
+    @Override protected void configure(Classpath xmlClasspath) {
+        classpath.mergeXmlClasspath(xmlClasspath)
+    }
+
+    /**
+     * The source sets to be added to the classpath.
+     */
+    Iterable<SourceSet> getSourceSets() {
+        classpath.sourceSets
+    }
+
+    /**
+     * The source sets to be added to the classpath.
+     */
+    void setSourceSets(Iterable<SourceSet> sourceSets) {
+        classpath.sourceSets = sourceSets
+    }
+
+    /**
+     * The configurations which files are to be transformed into classpath entries.
+     */
+    Collection<Configuration> getPlusConfigurations() {
+        classpath.plusConfigurations
+    }
+
+    void setPlusConfigurations(Collection<Configuration> plusConfigurations) {
+        classpath.plusConfigurations = plusConfigurations
+    }
+
+    /**
+     * The configurations which files are to be excluded from the classpath entries.
+     */
+    Collection<Configuration> getMinusConfigurations() {
+        classpath.minusConfigurations
+    }
+
+    void setMinusConfigurations(Collection<Configuration> minusConfigurations) {
+        classpath.minusConfigurations = minusConfigurations
+    }
+
+    /**
+     * Adds path variables to be used for replacing absolute paths in classpath entries.
+     *
+     * @param pathVariables A map with String->File pairs.
+     */
+    Map<String, File> getVariables() {
+        classpath.pathVariables
+    }
+
+    void setVariables(Map<String, File> variables) {
+        classpath.pathVariables = variables
+    }
+
+    /**
+     * Containers to be added to the classpath
+     */
+    Set<String> getContainers() {
+        classpath.containers
+    }
+
+    void setContainers(Set<String> containers) {
+        classpath.containers = containers
+    }
+
+    /**
+     * The default output directory for eclipse generated files, eg classes.
+     */
+    File getDefaultOutputDir() {
+        classpath.classesOutputDir
+    }
+
+    void setDefaultOutputDir(File defaultOutputDir) {
+        classpath.classesOutputDir = defaultOutputDir
+    }
+
+    /**
+     * Whether to download and add sources associated with the dependency jars. Defaults to true.
+     */
+    boolean getDownloadSources() {
+        classpath.downloadSources
+    }
+
+    void setDownloadSources(boolean downloadSources) {
+        classpath.downloadSources = downloadSources
+    }
+
+    /**
+     * Whether to download and add javadocs associated with the dependency jars. Defaults to false.
+     */
+    boolean getDownloadJavadoc() {
+        classpath.downloadJavadoc
+    }
+
+    void setDownloadJavadoc(boolean downloadJavadoc) {
+        classpath.downloadJavadoc = downloadJavadoc
+    }
+
+    /**
+     * Adds containers to the .classpath.
+     *
+     * @param containers the container names to be added to the .classpath.
+     */
+    void containers(String... containers) {
+        classpath.containers(containers)
+    }
+
+    /**
+     * Adds variables to be used for replacing absolute paths in classpath entries.
+     *
+     * @param variables A map where the keys are the variable names and the values are the variable values.
+     */
+    void variables(Map<String, File> variables) {
+        assert variables != null
+        classpath.pathVariables.putAll variables
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseJdt.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseJdt.groovy
new file mode 100644
index 0000000..20d0a69
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseJdt.groovy
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.eclipse
+
+import org.gradle.api.JavaVersion
+import org.gradle.plugins.ide.api.GeneratorTask
+import org.gradle.plugins.ide.eclipse.model.EclipseJdt
+import org.gradle.plugins.ide.eclipse.model.Jdt
+import org.gradle.plugins.ide.internal.generator.generator.PersistableConfigurationObjectGenerator
+
+/**
+ * Generates the Eclipse JDT configuration file.
+ */
+class GenerateEclipseJdt extends GeneratorTask<Jdt> {
+
+    /**
+     * Eclipse project model that contains information needed for this task
+     */
+    EclipseJdt jdt
+
+    /**
+     * The source Java language level.
+     */
+    JavaVersion getSourceCompatibility() {
+        jdt.sourceCompatibility
+    }
+
+    void setSourceCompatibility(Object sourceCompatibility) {
+        jdt.sourceCompatibility = sourceCompatibility
+    }
+
+    /**
+     * The target JVM to generate {@code .class} files for.
+     */
+    JavaVersion getTargetCompatibility() {
+        jdt.targetCompatibility
+    }
+
+    void setTargetCompatibility(Object targetCompatibility) {
+        jdt.targetCompatibility = targetCompatibility
+    }
+
+    GenerateEclipseJdt() {
+        generator = new PersistableConfigurationObjectGenerator<Jdt>() {
+            Jdt create() {
+                return new Jdt()
+            }
+
+            void configure(Jdt jdt) {
+                jdt.sourceCompatibility = getJdt().sourceCompatibility
+                jdt.targetCompatibility = getJdt().targetCompatibility
+            }
+        }
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseProject.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseProject.groovy
new file mode 100644
index 0000000..1c02064
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseProject.groovy
@@ -0,0 +1,184 @@
+/*
+ * 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.plugins.ide.eclipse
+
+import org.gradle.plugins.ide.api.XmlGeneratorTask
+import org.gradle.plugins.ide.eclipse.model.BuildCommand
+import org.gradle.plugins.ide.eclipse.model.EclipseProject
+import org.gradle.plugins.ide.eclipse.model.Link
+import org.gradle.plugins.ide.eclipse.model.Project
+
+/**
+ * Generates an Eclipse <code>.project</code> file.
+ * <p>
+ * Example how to configure eclipse project generation:
+ * <pre autoTested=''>
+ * apply plugin: 'java'
+ * apply plugin: 'eclipse'
+ *
+ * eclipseProject {
+ *   //...
+ * }
+ * </pre>
+ * @author Hans Dockter
+ */
+class GenerateEclipseProject extends XmlGeneratorTask<Project> {
+
+    /**
+     * model for eclipse project (.project) generation
+     */
+    EclipseProject projectModel
+
+    GenerateEclipseProject() {
+        xmlTransformer.indentation = "\t"
+    }
+
+    @Override protected Project create() {
+        new Project(xmlTransformer)
+    }
+
+    @Override protected void configure(Project project) {
+        projectModel.mergeXmlProject(project);
+    }
+
+    /**
+     * Configures eclipse project name. It is <b>optional</b> because the task should configure it correctly for you.
+     * By default it will try to use the <b>project.name</b> or prefix it with a part of a <b>project.path</b>
+     * to make sure the moduleName is unique in the scope of a multi-module build.
+     * The 'uniqeness' of a module name is required for correct import
+     * into Eclipse and the task will make sure the name is unique.
+     * <p>
+     * The logic that makes sure project names are uniqe is available <b>since</b> 1.0-milestone-2
+     * <p>
+     * In case you need to override the default projectName this is the way to go:
+     * <pre autoTested=''>
+     * apply plugin: 'eclipse'
+     *
+     * eclipseProject {
+     *   projectName = 'some-important-project'
+     * }
+     * </pre>
+     */
+    String getProjectName() {
+        projectModel.name
+    }
+
+    void setProjectName(String projectName) {
+        projectModel.name = projectName
+    }
+
+    /**
+     * A comment used for the eclipse project
+     */
+    String getComment() {
+        projectModel.comment
+    }
+
+    void setComment(String comment) {
+        projectModel.comment = comment
+    }
+
+    /**
+     * The referenced projects of this Eclipse project.
+     */
+    Set<String> getReferencedProjects() {
+        projectModel.referencedProjects
+    }
+
+    void setReferencedProjects(Set<String> referencedProjects) {
+        projectModel.referencedProjects = referencedProjects
+    }
+
+    /**
+     * The natures to be added to this Eclipse project.
+     */
+    List<String> getNatures() {
+        projectModel.natures
+    }
+
+    void setNatures(List<String> natures) {
+        projectModel.natures = natures
+    }
+
+    /**
+     * The build commands to be added to this Eclipse project.
+     */
+    List<BuildCommand> getBuildCommands() {
+        projectModel.buildCommands
+    }
+
+    void setBuildCommands(List<BuildCommand> buildCommands) {
+        projectModel.buildCommands = buildCommands
+    }
+
+    /**
+     * The linked resources to be added to this Eclipse project.
+     */
+    Set<Link> getLinks() {
+        projectModel.linkedResources
+    }
+
+    void setLinks(Set<Link> links) {
+        projectModel.linkedResources = links
+    }
+
+    /**
+     * Adds natures entries to the eclipse project.
+     * @param natures the nature names
+     */
+    void natures(String... natures) {
+        projectModel.natures(natures)
+    }
+
+    /**
+     * Adds project references to the eclipse project.
+     *
+     * @param referencedProjects The name of the project references.
+     */
+    void referencedProjects(String... referencedProjects) {
+        projectModel.referencedProjects(referencedProjects)
+    }
+
+    /**
+     * Adds a build command with arguments to the eclipse project.
+     *
+     * @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)
+     */
+    void buildCommand(Map args, String buildCommand) {
+        projectModel.buildCommand(args, buildCommand)
+    }
+
+    /**
+     * Adds a build command to the eclipse project.
+     *
+     * @param buildCommand The name of the build command
+     * @see #buildCommand(Map, String)
+     */
+    void buildCommand(String buildCommand) {
+        projectModel.buildCommand(buildCommand)
+    }
+
+    /**
+     * Adds a link to the eclipse project.
+     *
+     * @param args A maps with the args for the link. Legal keys for the map are name, type, location and locationUri.
+     */
+    void link(Map<String, String> args) {
+        projectModel.linkedResource(args)
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpComponent.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpComponent.groovy
new file mode 100644
index 0000000..ccbeb7d
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpComponent.groovy
@@ -0,0 +1,161 @@
+/*
+ * 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.plugins.ide.eclipse
+
+import org.gradle.api.artifacts.Configuration
+import org.gradle.plugins.ide.api.XmlGeneratorTask
+import org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent
+import org.gradle.plugins.ide.eclipse.model.WbProperty
+import org.gradle.plugins.ide.eclipse.model.WbResource
+import org.gradle.plugins.ide.eclipse.model.WtpComponent
+
+/**
+ * Generates the org.eclipse.wst.common.component settings file for Eclipse WTP.
+ *
+ * @author Hans Dockter
+ */
+class GenerateEclipseWtpComponent extends XmlGeneratorTask<WtpComponent> {
+
+    EclipseWtpComponent component
+
+    GenerateEclipseWtpComponent() {
+        xmlTransformer.indentation = "\t"
+    }
+
+    @Override protected WtpComponent create() {
+        new WtpComponent(xmlTransformer)
+    }
+
+    @Override protected void configure(WtpComponent xmlComponent) {
+        component.mergeXmlComponent(xmlComponent)
+    }
+
+    /**
+     * The source directories to be transformed into wb-resource elements.
+     */
+    Set<File> getSourceDirs() {
+        component.sourceDirs
+    }
+
+    void setSourceDirs(Set<File> sourceDirs) {
+        component.sourceDirs = sourceDirs
+    }
+
+    /**
+     * The configurations whose files are to be transformed into dependent-module elements.
+     */
+    Set<Configuration> getPlusConfigurations() {
+        component.plusConfigurations
+    }
+
+    void setPlusConfigurations(Set<Configuration> plusConfigurations) {
+        component.plusConfigurations = plusConfigurations
+    }
+
+    /**
+     * The configurations whose files are to be excluded from dependent-module elements.
+     */
+    Set<Configuration> getMinusConfigurations() {
+        component.minusConfigurations
+    }
+
+    void setMinusConfigurations(Set<Configuration> minusConfigurations) {
+        component.minusConfigurations = minusConfigurations
+    }
+
+    /**
+     * The deploy name to be used.
+     */
+    String getDeployName() {
+        component.deployName
+    }
+
+    void setDeployName(String deployName) {
+        component.deployName = deployName
+    }
+
+    /**
+     * The variables to be used for replacing absolute path in dependent-module elements.
+     */
+    Map<String, File> getVariables() {
+        component.pathVariables
+    }
+
+    void setVariables(Map<String, File> variables) {
+        component.pathVariables = variables
+    }
+
+    /**
+     * Additional wb-resource elements.
+     */
+    List<WbResource> getResources() {
+        component.resources
+    }
+
+    void setResources(List<WbResource> resources) {
+        component.resources = resources
+    }
+
+    /**
+     * Additional property elements.
+     */
+    List<WbProperty> getProperties() {
+        component.properties
+    }
+
+    void setProperties(List<WbProperty> properties) {
+        component.properties = properties
+    }
+
+    /**
+     * The context path for the web application
+     */
+    String getContextPath() {
+        component.contextPath
+    }
+
+    void setContextPath(String contextPath) {
+        component.contextPath = contextPath
+    }
+
+    /**
+     * Adds variables to be used for replacing absolute path in dependent-module elements.
+     *
+     * @param variables A map where the keys are the variable names and the values are the variable values.
+     */
+    void variables(Map<String, File> variables) {
+        assert variables != null
+        component.pathVariables.putAll variables
+    }
+
+    /**
+     * Adds a property.
+     *
+     * @param args A map that must contain a name and value key with corresponding values.
+     */
+    void property(Map<String, String> args) {
+        component.property(args)
+    }
+
+    /**
+     * Adds a wb-resource.
+     *
+     * @param args A map that must contain a deployPath and sourcePath key with corresponding values.
+     */
+    void resource(Map<String, String> args) {
+        component.resource(args)
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpFacet.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpFacet.groovy
new file mode 100644
index 0000000..c153008
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpFacet.groovy
@@ -0,0 +1,63 @@
+/*
+ * 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.plugins.ide.eclipse
+
+import org.gradle.plugins.ide.api.XmlGeneratorTask
+import org.gradle.plugins.ide.eclipse.model.EclipseWtpFacet
+import org.gradle.plugins.ide.eclipse.model.Facet
+import org.gradle.plugins.ide.eclipse.model.WtpFacet
+
+/**
+ * Generates the org.eclipse.wst.common.project.facet.core settings file for Eclipse WTP.
+ *
+ * @author Hans Dockter
+ */
+class GenerateEclipseWtpFacet extends XmlGeneratorTask<WtpFacet> {
+
+    EclipseWtpFacet facet
+
+    GenerateEclipseWtpFacet() {
+        xmlTransformer.indentation = "\t"
+    }
+
+    @Override protected WtpFacet create() {
+        new WtpFacet(xmlTransformer)
+    }
+
+    @Override protected void configure(WtpFacet xmlFacet) {
+        facet.mergeXmlFacet(xmlFacet)
+    }
+
+    /**
+     * The facets to be added as elements.
+     */
+    List<Facet> getFacets() {
+        facet.facets
+    }
+
+    void setFacets(List<Facet> facets) {
+        facet.facets = facets
+    }
+
+    /**
+     * Adds a facet.
+     *
+     * @param args A map that must contain a 'name' and 'version' key with corresponding values.
+     */
+    void facet(Map<String, ?> args) {
+        facet.facet(args)
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/internal/EclipseNameDeduper.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/internal/EclipseNameDeduper.groovy
new file mode 100644
index 0000000..a619e2a
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/internal/EclipseNameDeduper.groovy
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.eclipse.internal
+
+import org.gradle.api.Project
+import org.gradle.plugins.ide.eclipse.EclipsePlugin
+import org.gradle.plugins.ide.internal.configurer.DeduplicationTarget
+import org.gradle.plugins.ide.internal.configurer.ProjectDeduper
+
+/**
+ * @author Szczepan Faber, @date 11.03.11
+ */
+class EclipseNameDeduper {
+
+    void configure(Project aProject) {
+        def eclipseProjects = aProject.rootProject.allprojects.findAll { it.plugins.hasPlugin(EclipsePlugin) }
+        new ProjectDeduper().dedupe(eclipseProjects, { project ->
+            new DeduplicationTarget(project: project,
+                    moduleName: project.eclipseProject.projectModel.name,
+                    updateModuleName: { project.eclipseProject.projectModel.name = it })
+        })
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/AbstractClasspathEntry.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/AbstractClasspathEntry.groovy
new file mode 100644
index 0000000..a15cce3
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/AbstractClasspathEntry.groovy
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.eclipse.model
+
+import org.gradle.plugins.ide.eclipse.model.internal.PathUtil
+
+/**
+ * @author Hans Dockter
+ */
+// TODO: consider entryAttributes in equals, hashCode, and toString
+abstract class AbstractClasspathEntry implements ClasspathEntry {
+    private static final String NATIVE_LIBRARY_ATTRIBUTE = 'org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY'
+
+    String path
+    boolean exported
+    final Set<AccessRule> accessRules
+    final Map entryAttributes
+
+    AbstractClasspathEntry(Node node) {
+        path = normalizePath(node. at path)
+        exported = node. at exported
+        accessRules = readAccessRules(node)
+        entryAttributes = readEntryAttributes(node)
+        assert path != null && accessRules != null
+    }
+
+    AbstractClasspathEntry(String path, boolean exported, String nativeLibraryLocation, Set accessRules) {
+        assert path != null && accessRules != null
+        this.path = normalizePath(path);
+        this.exported = exported
+        this.accessRules = accessRules
+        entryAttributes = [:]
+        this.nativeLibraryLocation = nativeLibraryLocation
+    }
+
+    String getNativeLibraryLocation() {
+        entryAttributes.get(NATIVE_LIBRARY_ATTRIBUTE)
+    }
+
+    final void setNativeLibraryLocation(String location) {
+        entryAttributes.put(NATIVE_LIBRARY_ATTRIBUTE, location)
+    }
+
+    void appendNode(Node node) {
+        addClasspathEntry(node, [:])
+    }
+
+    protected Node addClasspathEntry(Node node, Map attributes) {
+        def allAttributes = attributes.findAll { it.value } + [kind: getKind(), path: path]
+        if (exported && !(this instanceof SourceFolder)) {
+            allAttributes.exported = true
+        }
+        Node entryNode = node.appendNode('classpathentry', allAttributes)
+        writeAccessRules(entryNode)
+        writeEntryAttributes(entryNode)
+        entryNode
+    }
+
+    protected String normalizePath(String path) {
+        PathUtil.normalizePath(path)
+    }
+
+    private Set readAccessRules(Node node) {
+        node.accessrules.accessrule.collect { ruleNode ->
+            new AccessRule(ruleNode. at kind, ruleNode. at pattern)
+        }
+    }
+
+    private void writeAccessRules(Node node) {
+        if (!accessRules) {
+            return
+        }
+        Node accessRulesNode = null
+        if (node.accessrules.size() == 0) {
+            accessRulesNode = node.appendNode('accessrules')
+        } else {
+            accessRulesNode = node.accessrules[0]
+        }
+        accessRules.each { AccessRule rule ->
+            accessRulesNode.appendNode('accessrule', [kind: rule.kind, pattern: rule.pattern])
+        }
+    }
+
+    private Map readEntryAttributes(Node node) {
+        def attrs = [:]
+        node.attributes.attribute.each {
+            attrs.put(it. at name, it. at value)
+        }
+        attrs
+    }
+
+    void writeEntryAttributes(Node node) {
+        def effectiveEntryAttrs = entryAttributes.findAll { it.value }
+        if (!effectiveEntryAttrs) { return }
+
+        Node attributesNode = node.children().find { it.name()  == 'attributes' } ?: node.appendNode('attributes')
+        effectiveEntryAttrs.each { key, value ->
+            attributesNode.appendNode('attribute', [name: key, value: value])
+        }
+    }
+
+    boolean equals(o) {
+        if (this.is(o)) { return true }
+
+        if (getClass() != o.class) { return false }
+
+        AbstractClasspathEntry that = (AbstractClasspathEntry) o;
+
+        if (exported != that.exported) { return false }
+        if (accessRules != that.accessRules) { return false }
+        if (nativeLibraryLocation != that.nativeLibraryLocation) { return false }
+        if (path != that.path) { return false }
+
+        return true
+    }
+
+    int hashCode() {
+        int result;
+
+        result = path.hashCode();
+        result = 31 * result + (nativeLibraryLocation != null ? nativeLibraryLocation.hashCode() : 0);
+        result = 31 * result + (exported ? 1 : 0);
+        result = 31 * result + accessRules.hashCode();
+        return result;
+    }
+
+    public String toString() {
+        return "{" +
+                "path='" + path + '\'' +
+                ", nativeLibraryLocation='" + nativeLibraryLocation + '\'' +
+                ", exported=" + exported +
+                ", accessRules=" + accessRules +
+                '}';
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/AbstractLibrary.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/AbstractLibrary.groovy
new file mode 100644
index 0000000..c7d8949
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/AbstractLibrary.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.plugins.ide.eclipse.model
+
+/**
+ * @author Hans Dockter
+ */
+abstract class AbstractLibrary extends AbstractClasspathEntry {
+    String sourcePath
+
+    AbstractLibrary(Node node) {
+        super(node)
+        sourcePath = normalizePath(node. at sourcepath)
+    }
+
+    String getJavadocPath() {
+        normalizePath(entryAttributes.javadoc_location)
+    }
+
+    void setJavadocPath(String path) {
+        entryAttributes.javadoc_location = path
+    }
+
+    AbstractLibrary(String path, boolean exported, String nativeLibraryLocation, Set accessRules, String sourcePath,
+                        String javadocPath) {
+        super(path, exported, nativeLibraryLocation, accessRules)
+        this.sourcePath = normalizePath(sourcePath);
+        this.javadocPath = normalizePath(javadocPath);
+    }
+
+    void appendNode(Node node) {
+        addClasspathEntry(node, [sourcepath: sourcePath])
+    }
+
+    boolean equals(o) {
+        if (this.is(o)) { return true }
+
+        if (getClass() != o.class) { return false }
+
+        AbstractLibrary that = (AbstractLibrary) o;
+
+        if (exported != that.exported) { return false }
+        if (accessRules != that.accessRules) { return false }
+        if (javadocPath != that.javadocPath) { return false }
+        if (nativeLibraryLocation != that.nativeLibraryLocation) { return false }
+        if (path != that.path) { return false }
+        if (sourcePath != that.sourcePath) { return false }
+
+        return true
+    }
+
+    int hashCode() {
+        int result;
+
+        result = path.hashCode();
+        result = 31 * result + (nativeLibraryLocation != null ? nativeLibraryLocation.hashCode() : 0);
+        result = 31 * result + (exported ? 1 : 0);
+        result = 31 * result + accessRules.hashCode();
+        result = 31 * result + (sourcePath != null ? sourcePath.hashCode() : 0);
+        result = 31 * result + (javadocPath != null ? javadocPath.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "{" +
+                "path='" + path + '\'' +
+                ", nativeLibraryLocation='" + nativeLibraryLocation + '\'' +
+                ", exported=" + exported +
+                ", accessRules=" + accessRules +
+                ", sourcePath='" + sourcePath + '\'' +
+                ", javadocPath='" + javadocPath + '\'' +
+                '}';
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/AccessRule.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/AccessRule.groovy
new file mode 100644
index 0000000..39068cb
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/AccessRule.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.plugins.ide.eclipse.model
+
+/**
+ * @author Hans Dockter
+ */
+class AccessRule {
+    String kind
+    String pattern
+
+    def AccessRule(kind, pattern) {
+        assert kind != null && pattern != null
+        this.kind = kind;
+        this.pattern = pattern;
+    }
+
+    boolean equals(o) {
+        if (this.is(o)) { return true }
+
+        if (getClass() != o.class) { return false }
+
+        AccessRule that = (AccessRule) o;
+
+        if (kind != that.kind) { return false }
+        if (pattern != that.pattern) { return false }
+
+        return true
+    }
+
+    int hashCode() {
+        int result;
+
+        result = kind.hashCode();
+        result = 31 * result + pattern.hashCode();
+        return result;
+    }
+
+    public String toString() {
+        return "AccessRule{" +
+                "kind='" + kind + '\'' +
+                ", pattern='" + pattern + '\'' +
+                '}';
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/BuildCommand.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/BuildCommand.groovy
new file mode 100644
index 0000000..f84cde8
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/BuildCommand.groovy
@@ -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.plugins.ide.eclipse.model
+
+/**
+ * @author Hans Dockter
+ */
+class BuildCommand implements Serializable {
+    String name
+    Map arguments
+
+    def BuildCommand(String name, Map arguments = [:]) {
+        assert name != null
+        assert arguments != null
+        this.name = name
+        this.arguments = arguments
+    }
+
+    boolean equals(o) {
+        if (this.is(o)) { return true }
+
+        if (getClass() != o.class) { return false }
+
+        BuildCommand that = (BuildCommand) o;
+
+        if (arguments != that.arguments) { return false }
+        if (name != that.name) { return false }
+
+        return true
+    }
+
+    int hashCode() {
+        int result;
+
+        result = name.hashCode();
+        result = 31 * result + arguments.hashCode();
+        return result;
+    }
+
+
+    public String toString() {
+        return "BuildCommand{" +
+                "name='" + name + '\'' +
+                ", arguments=" + arguments +
+                '}';
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Classpath.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Classpath.groovy
new file mode 100644
index 0000000..9c6892f
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Classpath.groovy
@@ -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.plugins.ide.eclipse.model
+
+import org.gradle.api.internal.XmlTransformer
+import org.gradle.plugins.ide.internal.generator.XmlPersistableConfigurationObject
+
+/**
+ * Represents the customizable elements of an eclipse classpath file. (via XML hooks everything is customizable).
+ *
+ * @author Hans Dockter
+ */
+class Classpath extends XmlPersistableConfigurationObject {
+    List<ClasspathEntry> entries = []
+
+    Classpath(XmlTransformer xmlTransformer) {
+        super(xmlTransformer)
+    }
+
+    Classpath() {
+        super(new XmlTransformer())
+    }
+
+    @Override protected String getDefaultResourceName() {
+        return 'defaultClasspath.xml'
+    }
+
+    @Override protected void load(Node xml) {
+        xml.classpathentry.each { Node entryNode ->
+            ClasspathEntry entry = null
+            switch (entryNode. at kind) {
+                case 'src':
+                    def path = entryNode. at path
+                    entry = path.startsWith('/') ? new ProjectDependency(entryNode) : new SourceFolder(entryNode)
+                    break
+                case 'var': entry = new Variable(entryNode)
+                    break
+                case 'con': entry = new Container(entryNode)
+                    break
+                case 'lib': entry = new Library(entryNode)
+                    break
+                case 'output': entry = new Output(entryNode)
+                    break
+            }
+            if (entry) {
+                entries.add(entry)
+            }
+        }
+    }
+
+    def configure(List newEntries) {
+        def entriesToBeKept = entries.findAll { !isDependency(it) }
+        entries = (entriesToBeKept + newEntries).unique()
+    }
+
+    @Override protected void store(Node xml) {
+        xml.classpathentry.each { xml.remove(it) }
+        entries.each { ClasspathEntry entry ->
+            entry.appendNode(xml)
+        }
+    }
+
+    boolean equals(o) {
+        if (this.is(o)) { return true }
+
+        if (getClass() != o.class) { return false }
+
+        Classpath classpath = (Classpath) o;
+
+        if (entries != classpath.entries) { return false }
+
+        return true
+    }
+
+    int hashCode() {
+        int result;
+
+        result = entries.hashCode();
+        return result;
+    }
+
+    public String toString() {
+        return "Classpath{" +
+                "entries=" + entries +
+                '}';
+    }
+
+    private boolean isDependency(ClasspathEntry entry) {
+        entry instanceof ProjectDependency || entry instanceof Library
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/ClasspathEntry.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/ClasspathEntry.java
new file mode 100644
index 0000000..e4349b2
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/ClasspathEntry.java
@@ -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.ide.eclipse.model;
+
+import groovy.util.Node;
+
+/**
+ * Represents an entry in the Eclipse classpath.
+ * 
+ * @author Hans Dockter
+ */
+public interface ClasspathEntry {
+    String getKind();
+    void appendNode(Node node);
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Container.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Container.groovy
new file mode 100644
index 0000000..a05a243
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Container.groovy
@@ -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.plugins.ide.eclipse.model
+
+/**
+ * @author Hans Dockter
+ */
+class Container extends AbstractClasspathEntry {
+    Container(node) {
+        super(node);
+    }
+
+    Container(String path, boolean exported, String nativeLibraryLocation, Set accessRules) {
+        super(path, exported, nativeLibraryLocation, accessRules)
+    }
+
+    String getKind() {
+        'con'
+    }
+
+    public String toString() {
+        return "Container{" + super.toString()
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseClasspath.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseClasspath.groovy
new file mode 100644
index 0000000..86aafbc
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseClasspath.groovy
@@ -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.plugins.ide.eclipse.model
+
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.tasks.SourceSet
+import org.gradle.plugins.ide.eclipse.model.internal.ClasspathFactory
+
+/**
+ * DSL-friendly model of the eclipse classpath needed for .classpath generation
+ * <p>
+ * Example of use with a blend of all possible properties.
+ * Bear in mind that usually you don't have configure eclipse classpath directly because Gradle configures it for free!
+ *
+ * <pre autoTested=''>
+ * apply plugin: 'java'
+ * apply plugin: 'eclipse'
+ *
+ * configurations {
+ *   provided
+ *   someBoringConfig
+ * }
+ *
+ * eclipse {
+ *
+ *   //if you want parts of paths in resulting file to be replaced by variables (files):
+ *   pathVariables 'GRADLE_HOME': file('/best/software/gradle'), 'TOMCAT_HOME': file('../tomcat')
+ *
+ *
+ *   classpath {
+ *     //you can configure the sourceSets however Gradle simply uses current sourceSets
+ *     //so it's probably best not to change it.
+ *     //sourceSets =
+ *
+ *     //you can tweak the classpath of the eclipse project by adding extra configurations:
+ *     plusConfigurations += configurations.provided
+ *
+ *     //you can also remove configurations from the classpath:
+ *     minusConfigurations += configurations.someBoringConfig
+ *
+ *     //if you want to append extra containers:
+ *     containers 'someFriendlyContainer', 'andYetAnotherContainer'
+ *
+ *     //customizing the classes output directory:
+ *     classesOutputDir = file('build-eclipse')
+ *
+ *     //default settings for dependencies sources/javadoc download:
+ *     downloadSources = true
+ *     downloadJavadoc = false
+ *   }
+ * }
+ * </pre>
+ *
+ * Author: Szczepan Faber, created at: 4/16/11
+ */
+class EclipseClasspath {
+
+    /**
+     * The source sets to be added to the classpath.
+     * <p>
+     * For example see docs for {@link EclipseClasspath}
+     */
+    Iterable<SourceSet> sourceSets
+
+    /**
+     * The configurations which files are to be transformed into classpath entries.
+     * <p>
+     * For example see docs for {@link EclipseClasspath}
+     */
+    Collection<Configuration> plusConfigurations = []
+
+    /**
+     * The configurations which files are to be excluded from the classpath entries.
+     * <p>
+     * For example see docs for {@link EclipseClasspath}
+     */
+    Collection<Configuration> minusConfigurations = []
+
+   /**
+     * Containers to be added to the classpath
+     * <p>
+     * For example see docs for {@link EclipseClasspath}
+     */
+    Set<String> containers = new LinkedHashSet<String>()
+
+   /**
+     * Adds containers to the .classpath.
+     * <p>
+     * For example see docs for {@link EclipseClasspath}
+     *
+     * @param containers the container names to be added to the .classpath.
+     */
+    void containers(String... containers) {
+        assert containers != null
+        this.containers.addAll(containers as List)
+    }
+
+    /**
+     * The default output directory for eclipse generated files, eg classes.
+     * <p>
+     * For example see docs for {@link EclipseClasspath}
+     */
+    File classesOutputDir
+
+    /**
+     * Whether to download and add sources associated with the dependency jars. Defaults to true.
+     * <p>
+     * For example see docs for {@link EclipseClasspath}
+     */
+    boolean downloadSources = true
+
+    /**
+     * Whether to download and add javadocs associated with the dependency jars. Defaults to false.
+     * <p>
+     * For example see docs for {@link EclipseClasspath}
+     */
+    boolean downloadJavadoc = false
+
+    /**
+     * Calculates, resolves & returns dependency entries of this classpath
+     */
+    public List<ClasspathEntry> resolveDependencies() {
+        return new ClasspathFactory().createEntries(this)
+    }
+
+    /******/
+
+    org.gradle.api.Project project
+
+    /**
+     * The path variables to be used for replacing absolute paths in classpath entries.
+     * <p>
+     * For example see docs for {@link EclipseModel}
+     */
+    Map<String, File> pathVariables = [:]
+
+    /**
+     * Modifies the content of plusConfigurations and minusConfigurations by filtering dependencies
+     *
+     * @param projectDependenciesOnly true - only project dependencies, false - no filter
+     */
+    public void setProjectDependenciesOnly(boolean projectDependenciesOnly) {
+        if (projectDependenciesOnly) {
+            onlyProject = { it instanceof org.gradle.api.artifacts.ProjectDependency }
+            plusConfigurations = plusConfigurations.collect { it.copyRecursive { dependency -> onlyProject(dependency) }}
+            minusConfigurations = minusConfigurations.collect { it.copyRecursive { dependency -> onlyProject(dependency) }}
+        }
+    }
+
+    void mergeXmlClasspath(Classpath xmlClasspath) {
+        def entries = resolveDependencies()
+        xmlClasspath.configure(entries)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseDomainModel.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseDomainModel.groovy
new file mode 100644
index 0000000..0e2ecce
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseDomainModel.groovy
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.eclipse.model
+
+/**
+ * For now, we only need the Project. However this class will contain more domain objects soon.
+ *
+ * @author Szczepan Faber, @date: 17.03.11
+ */
+class EclipseDomainModel {
+    Project project
+    Classpath classpath
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseJdt.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseJdt.groovy
new file mode 100644
index 0000000..f050772
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseJdt.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.plugins.ide.eclipse.model
+
+import org.gradle.api.JavaVersion
+
+/**
+ * Models the java compatibility information
+ * <p>
+ * For example see docs for {@link EclipseProject}
+ *
+ * @author: Szczepan Faber, created at: 4/20/11
+ */
+class EclipseJdt {
+
+    /**
+     * The source Java language level.
+     * <p>
+     * For example see docs for {@link EclipseProject}
+     */
+    JavaVersion sourceCompatibility
+
+    void setSourceCompatibility(Object sourceCompatibility) {
+        this.sourceCompatibility = JavaVersion.toVersion(sourceCompatibility)
+    }
+
+    /**
+     * The target JVM to generate {@code .class} files for.
+     * <p>
+     * For example see docs for {@link EclipseProject}
+     */
+    JavaVersion targetCompatibility
+
+    void setTargetCompatibility(Object targetCompatibility) {
+        this.targetCompatibility = JavaVersion.toVersion(targetCompatibility)
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseModel.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseModel.groovy
new file mode 100644
index 0000000..25ab494
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseModel.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.plugins.ide.eclipse.model
+
+import org.gradle.util.ConfigureUtil
+
+/**
+ * DSL-friendly model of the Eclipse project information.
+ * First point of entry when it comes to customizing the eclipse generation
+ *
+ * <pre autoTested=''>
+ * apply plugin: 'java'
+ * apply plugin: 'war'  //needed for wtp
+ * apply plugin: 'eclipse'
+ *
+ * eclipse {
+ *   pathVariables 'GRADLE_HOME': file('/best/software/gradle'), 'TOMCAT_HOME': file('../tomcat')
+ *
+ *   project {
+ *     //see docs for {@link EclipseProject}
+ *   }
+ *
+ *   classpath {
+ *     //see docs for {@link EclipseClasspath}
+ *   }
+ *
+ *   wtp {
+ *     //see docs for {@link EclipseWtp}
+ *   }
+ * }
+ * </pre>
+ *
+ * More examples in docs for {@link EclipseProject}, {@link EclipseClasspath}, {@link EclipseWtp}
+ *
+ * @author: Szczepan Faber, created at: 4/13/11
+ */
+class EclipseModel {
+
+    EclipseProject project
+    EclipseClasspath classpath
+    EclipseJdt jdt
+
+    EclipseWtp wtp = new EclipseWtp()
+
+    /**
+     * Configures eclipse project
+     * <p>
+     * For examples see docs for {@link EclipseProject}
+     *
+     * @param closure
+     */
+    void project(Closure closure) {
+        ConfigureUtil.configure(closure, project)
+    }
+
+    /**
+     * Configures eclipse classpath information
+     * <p>
+     * For examples see docs for {@link EclipseClasspath}
+     *
+     * @param closure
+     */
+    void classpath(Closure closure) {
+        ConfigureUtil.configure(closure, classpath)
+    }
+
+    /**
+     * Configures eclipse wtp information
+     * <p>
+     * For examples see docs for {@link EclipseWtp}
+     *
+     * @param closure
+     */
+    void wtp(Closure closure) {
+        ConfigureUtil.configure(closure, wtp)
+    }
+
+    /**
+     * Configures eclipse java compatibility information (jdt)
+     * <p>
+     * For examples see docs for {@link EclipseProject}
+     *
+     * @param closure
+     */
+    void jdt(Closure closure) {
+        ConfigureUtil.configure(closure, jdt)
+    }
+
+    /**
+     * Adds path variables to be used for replacing absolute paths in classpath entries.
+     * <p>
+     * For example see docs for {@link EclipseModel}
+     *
+     * @param pathVariables A map with String->File pairs.
+     */
+    void pathVariables(Map<String, File> pathVariables) {
+        assert pathVariables != null
+        classpath.pathVariables.putAll pathVariables
+        if (wtp.component) {
+            wtp.component.pathVariables.putAll pathVariables
+        }
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseProject.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseProject.groovy
new file mode 100644
index 0000000..0809f70
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseProject.groovy
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.eclipse.model
+
+import org.gradle.api.InvalidUserDataException
+
+/**
+ * DSL-friendly model of the eclipse project needed for .project generation
+ * <p>
+ * Example of use with a blend of all possible properties.
+ * Bear in mind that usually you don't have configure eclipse project directly because Gradle configures it for free!
+ *
+ * <pre autoTested=''>
+ * apply plugin: 'java'
+ * apply plugin: 'eclipse'
+ *
+ * eclipse {
+ *   project {
+ *     //if you don't like the name Gradle has chosen
+ *     name = 'someBetterName'
+ *
+ *     //if you want to specify the Eclipse project's comment
+ *     comment = 'Very interesting top secret project'
+ *
+ *     //if you want to append some extra referenced projects in a declarative fashion:
+ *     referencedProjects 'someProject', 'someOtherProject'
+ *     //if you want to assign referenced projects
+ *     referencedProjects = ['someProject'] as Set
+ *
+ *     //if you want to append some extra natures in a declarative fashion:
+ *     natures 'some.extra.eclipse.nature', 'some.another.interesting.nature'
+ *     //if you want to assign natures in a groovy fashion:
+ *     natures = ['some.extra.eclipse.nature', 'some.another.interesting.nature']
+ *
+ *     //if you want to append some extra build command:
+ *     buildCommand 'buildThisLovelyProject'
+ *     //if you want to append a build command with parameters:
+ *     buildCommand argumentOne: "I'm first", argumentTwo: "I'm second", 'buildItWithTheArguments'
+ *
+ *     //if you want to create an extra link in the eclipse project,
+ *     //by location uri:
+ *     linkedResource name: 'someLinkByLocationUri', type: 'someLinkType', locationUri: 'file://someUri'
+ *     //by location:
+ *     linkedResource name: 'someLinkByLocation', type: 'someLinkType', location: '/some/location'
+ *   }
+ *
+ *   jdt {
+ *     //if you want to alter the java versions (by default they are configured with gradle java plugin settings):
+ *     sourceCompatibility = 1.6
+ *     targetCompatibility = 1.5
+ *   }
+ * }
+ * </pre>
+ *
+ * Author: Szczepan Faber, created at: 4/13/11
+ */
+class EclipseProject {
+
+    /**
+     * Configures eclipse project name. It is <b>optional</b> because the task should configure it correctly for you.
+     * By default it will try to use the <b>project.name</b> or prefix it with a part of a <b>project.path</b>
+     * to make sure the moduleName is unique in the scope of a multi-module build.
+     * The 'uniqeness' of a module name is required for correct import
+     * into Eclipse and the task will make sure the name is unique.
+     * <p>
+     * The logic that makes sure project names are uniqe is available <b>since</b> 1.0-milestone-2
+     * <p>
+     * For example see docs for {@link EclipseProject}
+     */
+    String name
+
+    /**
+     * A comment used for the eclipse project. By default it will be configured to <b>project.description</b>
+     * <p>
+     * For example see docs for {@link EclipseProject}
+     */
+    String comment
+
+    /**
+     * The referenced projects of this Eclipse project.
+     * <p>
+     * For example see docs for {@link EclipseProject}
+     */
+    Set<String> referencedProjects = new LinkedHashSet<String>()
+
+    /**
+     * Adds project references to the eclipse project.
+     *
+     * @param referencedProjects The name of the project references.
+     */
+    void referencedProjects(String... referencedProjects) {
+        assert referencedProjects != null
+        this.referencedProjects.addAll(referencedProjects as List)
+    }
+
+    /**
+     * The natures to be added to this Eclipse project.
+     * <p>
+     * For example see docs for {@link EclipseProject}
+     */
+    List<String> natures = []
+
+    /**
+     * Appends natures entries to the eclipse project.
+     * <p>
+     * For example see docs for {@link EclipseProject}
+     *
+     * @param natures the nature names
+     */
+    void natures(String... natures) {
+        assert natures != null
+        this.natures.addAll(natures as List)
+    }
+
+    /**
+     * The build commands to be added to this Eclipse project.
+     * <p>
+     * For example see docs for {@link EclipseProject}
+     */
+    List<BuildCommand> buildCommands = []
+
+    /**
+     * Adds a build command with arguments to the eclipse project.
+     * <p>
+     * For example see docs for {@link EclipseProject}
+     *
+     * @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)
+     */
+    void buildCommand(Map args, String buildCommand) {
+        assert buildCommand != null
+        buildCommands << new BuildCommand(buildCommand, args)
+    }
+
+    /**
+     * Adds a build command to the eclipse project.
+     * <p>
+     * For example see docs for {@link EclipseProject}
+     *
+     * @param buildCommand The name of the build command
+     * @see #buildCommand(Map, String)
+     */
+    void buildCommand(String buildCommand) {
+        assert buildCommand != null
+        buildCommands << new BuildCommand(buildCommand)
+    }
+
+    /**
+     * The linked resources to be added to this Eclipse project.
+     * <p>
+     * For example see docs for {@link EclipseProject}
+     */
+    Set<Link> linkedResources = new LinkedHashSet<Link>()
+
+    /**
+     * Adds a resource link (aka 'source link') to the eclipse project.
+     * <p>
+     * For example see docs for {@link EclipseProject}
+     *
+     * @param args A maps with the args for the link. Legal keys for the map are name, type, location and locationUri.
+     */
+    void linkedResource(Map<String, String> args) {
+        def validKeys = ['name', 'type', 'location', 'locationUri']
+        def illegalArgs = args.keySet() - validKeys
+        if (illegalArgs) {
+            throw new InvalidUserDataException("You provided illegal argument for a link: $illegalArgs. Valid link args are: $validKeys")
+        }
+        //TODO SF: move validation here, update tests
+        linkedResources << new Link(args.name, args.type, args.location, args.locationUri)
+    }
+
+    /*****/
+
+    void mergeXmlProject(Project xmlProject) {
+        xmlProject.configure(this)
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseWtp.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseWtp.groovy
new file mode 100644
index 0000000..7432497
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseWtp.groovy
@@ -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.plugins.ide.eclipse.model
+
+import org.gradle.util.ConfigureUtil
+
+/**
+ * Dsl-friendly model of the eclipse wtp information
+ * <p>
+ * Example of use with a blend of all possible properties.
+ * Bear in mind that usually you don't have configure them directly because Gradle configures it for free!
+ *
+ * <pre autoTested=''>
+ * apply plugin: 'java'
+ * apply plugin: 'war'
+ * apply plugin: 'eclipse'
+ *
+ * configurations {
+ *   someInterestingConfiguration
+ *   anotherConfiguration
+ * }
+ *
+ * eclipse {
+ *
+ *   //if you want parts of paths in resulting file(s) to be replaced by variables (files):
+ *   pathVariables 'GRADLE_HOME': file('/best/software/gradle'), 'TOMCAT_HOME': file('../tomcat')
+ *
+ *   wtp {
+ *     component {
+ *       //you can configure the context path:
+ *       contextPath = 'someContextPath'
+ *
+ *       //you can configure the deployName:
+ *       deployName = 'killerApp'
+ *
+ *       //you can alter the wb-resource elements. sourceDirs is a ConvenienceProperty.
+ *       sourceDirs += file('someExtraFolder')
+ *
+ *       //you can alter the files are to be transformed into dependent-module elements:
+ *       plusConfigurations += configurations.someInterestingConfiguration
+ *
+ *       //or whose files are to be excluded from dependent-module elements:
+ *       minusConfigurations += configurations.anotherConfiguration
+ *
+ *       //you can add a wb-resource elements; mandatory keys: 'sourcePath', 'deployPath':
+ *       resource sourcePath: 'extra/resource', deployPath: 'deployment/resource'
+ *
+ *       //you can add a wb-property elements; mandatory keys: 'name', 'value':
+ *       property name: 'moodOfTheDay', value: ':-D'
+ *     }
+ *
+ *     facet {
+ *       //you can add some extra wtp facets; mandatory keys: 'name', 'version':
+ *       facet name: 'someCoolFacet', version: '1.3'
+ *     }
+ *   }
+ * }
+ *
+ * </pre>
+ *
+ * @author: Szczepan Faber, created at: 4/19/11
+ */
+class EclipseWtp {
+
+    EclipseWtpComponent component
+    EclipseWtpFacet facet
+
+    /**
+     * Configures wtp component.
+     * <p>
+     * For examples see docs for {@link EclipseWtp}
+     *
+     * @param action
+     */
+    void component(Closure action) {
+        ConfigureUtil.configure(action, component)
+    }
+
+    /**
+     * Configures wtp facet.
+     * <p>
+     * For examples see docs for {@link EclipseWtp}
+     *
+     * @param action
+     */
+    void facet(Closure action) {
+        ConfigureUtil.configure(action, facet)
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseWtpComponent.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseWtpComponent.groovy
new file mode 100644
index 0000000..60fc191
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseWtpComponent.groovy
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.eclipse.model
+
+import org.gradle.api.artifacts.Configuration
+import org.gradle.plugins.ide.eclipse.model.internal.WtpComponentFactory
+
+/**
+ * Models the information need for wtp component
+ * <p>
+ * For examples see docs for {@link EclipseWtp}
+ *
+ * @author: Szczepan Faber, created at: 4/20/11
+ */
+class EclipseWtpComponent {
+
+    /**
+     * The source directories to be transformed into wb-resource elements.
+     * <p>
+     * Warning, this property is a {@link org.gradle.api.dsl.ConvenienceProperty}
+     * <p>
+     * For examples see docs for {@link EclipseWtp}
+     */
+    Set<File> sourceDirs
+
+    /**
+     * The configurations whose files are to be transformed into dependent-module elements.
+     * <p>
+     * For examples see docs for {@link EclipseWtp}
+     */
+    Set<Configuration> plusConfigurations
+
+    /**
+     * The configurations whose files are to be excluded from dependent-module elements.
+     * <p>
+     * For examples see docs for {@link EclipseWtp}
+     */
+    Set<Configuration> minusConfigurations
+
+    /**
+     * The deploy name to be used.
+     * <p>
+     * For examples see docs for {@link EclipseWtp}
+     */
+    String deployName
+
+    /**
+     * Additional wb-resource elements.
+     * <p>
+     * Warning, this property is a {@link org.gradle.api.dsl.ConvenienceProperty}
+     * <p>
+     * For examples see docs for {@link EclipseWtp}
+     */
+    List<WbResource> resources = []
+
+    /**
+     * Adds a wb-resource.
+     * <p>
+     * For examples see docs for {@link EclipseWtp}
+     *
+     * @param args A map that must contain a deployPath and sourcePath key with corresponding values.
+     */
+    void resource(Map<String, String> args) {
+        //TODO SF validation
+        resources.add(new WbResource(args.deployPath, args.sourcePath))
+    }
+
+    /**
+     * Additional property elements.
+     * <p>
+     * For examples see docs for {@link EclipseWtp}
+     */
+    List<WbProperty> properties = []
+
+    /**
+     * Adds a property.
+     * <p>
+     * For examples see docs for {@link EclipseWtp}
+     *
+     * @param args A map that must contain a 'name' and 'value' key with corresponding values.
+     */
+    void property(Map<String, String> args) {
+        //TODO SF validation
+        properties.add(new WbProperty(args.name, args.value))
+    }
+
+   /**
+     * The context path for the web application
+     * <p>
+     * For examples see docs for {@link EclipseWtp}
+     */
+    String contextPath
+
+    //********
+
+    org.gradle.api.Project project
+
+    /**
+     * The variables to be used for replacing absolute path in dependent-module elements.
+     * <p>
+     * For examples see docs for {@link EclipseModel}
+     */
+    Map<String, File> pathVariables = [:]
+
+    void mergeXmlComponent(WtpComponent xmlComponent) {
+        new WtpComponentFactory().configure(this, xmlComponent)
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseWtpFacet.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseWtpFacet.groovy
new file mode 100644
index 0000000..9aa1686
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseWtpFacet.groovy
@@ -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.plugins.ide.eclipse.model
+
+import org.gradle.util.ConfigureUtil
+
+/**
+ * Models the information need for wtp component
+ * <p>
+ * For examples see docs for {@link EclipseWtp}
+ *
+ * @author: Szczepan Faber, created at: 4/20/11
+ */
+class EclipseWtpFacet {
+
+    /**
+     * The facets to be added as elements.
+     * <p>
+     * For examples see docs for {@link EclipseWtp}
+     */
+    // TODO: What's the difference between fixed and installed facets? Why do we only model the latter?
+    List<Facet> facets = []
+
+    /**
+     * Adds a facet.
+     * <p>
+     * For examples see docs for {@link EclipseWtp}
+     *
+     * @param args A map that must contain a 'name' and 'version' key with corresponding values.
+     */
+    void facet(Map<String, ?> args) {
+        facets << ConfigureUtil.configureByMap(args, new Facet())
+    }
+
+    /*****/
+
+    void mergeXmlFacet(WtpFacet xmlFacet) {
+        xmlFacet.configure(getFacets())
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Facet.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Facet.groovy
new file mode 100644
index 0000000..e5f9542
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Facet.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.ide.eclipse.model
+
+/**
+ * @author Hans Dockter
+ */
+
+class Facet {
+    String name
+    String version
+
+    def Facet() {
+    }
+
+    def Facet(Node node) {
+        this(node. at facet, node. at version)
+    }
+
+    def Facet(String name, String version) {
+        assert name != null && version != null
+        this.name = name
+        this.version = version
+    }
+
+    void appendNode(Node node) {
+        node.appendNode("installed", [facet: name, version: version])
+    }
+
+    boolean equals(o) {
+        if (this.is(o)) { return true }
+
+        if (getClass() != o.class) { return false }
+
+        Facet facet = (Facet) o;
+
+        if (name != facet.name) { return false }
+        if (version != facet.version) { return false }
+
+        return true
+    }
+
+    int hashCode() {
+        int result;
+
+        result = name.hashCode();
+        result = 31 * result + version.hashCode();
+        return result;
+    }
+
+    public String toString() {
+        return "Facet{" +
+                "name='" + name + '\'' +
+                ", version='" + version + '\'' +
+                '}';
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Jdt.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Jdt.java
new file mode 100644
index 0000000..ac00764
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Jdt.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.plugins.ide.eclipse.model;
+
+import org.gradle.api.JavaVersion;
+import org.gradle.plugins.ide.internal.generator.PropertiesPersistableConfigurationObject;
+
+import java.util.Properties;
+
+/**
+ * Represents the Eclipse JDT settings.
+ */
+public class Jdt extends PropertiesPersistableConfigurationObject {
+    private JavaVersion sourceCompatibility;
+    private JavaVersion targetCompatibility;
+
+    /**
+     * Sets the source compatibility for the compiler.
+     */
+    public void setSourceCompatibility(JavaVersion sourceCompatibility) {
+        this.sourceCompatibility = sourceCompatibility;
+    }
+
+    /**
+     * Sets the target compatibility for the compiler.
+     */
+    public void setTargetCompatibility(JavaVersion targetCompatibility) {
+        this.targetCompatibility = targetCompatibility;
+    }
+
+    @Override
+    protected String getDefaultResourceName() {
+        return "defaultJdtPrefs.properties";
+    }
+
+    @Override
+    protected void load(Properties properties) {
+    }
+
+    @Override
+    protected void store(Properties properties) {
+        properties.put("org.eclipse.jdt.core.compiler.compliance", sourceCompatibility.toString());
+        properties.put("org.eclipse.jdt.core.compiler.source", sourceCompatibility.toString());
+
+        if (sourceCompatibility.compareTo(JavaVersion.VERSION_1_3) <= 0) {
+            properties.put("org.eclipse.jdt.core.compiler.problem.assertIdentifier", "ignore");
+            properties.put("org.eclipse.jdt.core.compiler.problem.enumIdentifier", "ignore");
+        } else if (sourceCompatibility == JavaVersion.VERSION_1_4) {
+            properties.put("org.eclipse.jdt.core.compiler.problem.assertIdentifier", "error");
+            properties.put("org.eclipse.jdt.core.compiler.problem.enumIdentifier", "warning");
+        } else {
+            properties.put("org.eclipse.jdt.core.compiler.problem.assertIdentifier", "error");
+            properties.put("org.eclipse.jdt.core.compiler.problem.enumIdentifier", "error");
+        }
+
+        properties.put("org.eclipse.jdt.core.compiler.codegen.targetPlatform", targetCompatibility.toString());
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Library.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Library.groovy
new file mode 100644
index 0000000..2d2ffbf
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Library.groovy
@@ -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.plugins.ide.eclipse.model
+
+/**
+ * @author Hans Dockter
+ */
+class Library extends AbstractLibrary {
+    Library(Node node) {
+        super(node);
+    }
+
+    Library(String path, boolean exported, String nativeLibraryLocation, Set accessRules, String sourcePath, String javadocPath) {
+        super(path, exported, nativeLibraryLocation, accessRules, sourcePath, javadocPath)
+    }
+
+    String getKind() {
+        'lib'
+    }
+
+    public String toString() {
+        return "Library{" + super.toString()
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Link.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Link.groovy
new file mode 100644
index 0000000..a8e60b2
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Link.groovy
@@ -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.plugins.ide.eclipse.model
+
+import org.gradle.plugins.ide.eclipse.model.internal.PathUtil
+
+/**
+ * @author Hans Dockter
+ */
+class Link {
+    String name
+    String type
+    String location
+    String locationUri
+
+    def Link(String name, String type, String location, String locationUri) {
+        assert name
+        assert type
+        assert location || locationUri
+        assert !location || !locationUri
+        this.name = name;
+        this.type = type;
+        this.location = PathUtil.normalizePath(location);
+        this.locationUri = locationUri;
+    }
+
+
+    boolean equals(o) {
+        if (this.is(o)) { return true }
+
+        if (getClass() != o.class) { return false }
+
+        Link link = (Link) o;
+
+        if (location != link.location) { return false }
+        if (locationUri != link.locationUri) { return false }
+        if (name != link.name) { return false }
+        if (type != link.type) { return false }
+
+        return true
+    }
+
+    int hashCode() {
+        int result;
+
+        result = name.hashCode();
+        result = 31 * result + type.hashCode();
+        result = 31 * result + (location != null ? location.hashCode() : 0);
+        result = 31 * result + (locationUri != null ? locationUri.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Output.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Output.groovy
new file mode 100644
index 0000000..5a1b22c
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Output.groovy
@@ -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.plugins.ide.eclipse.model
+
+import org.gradle.plugins.ide.eclipse.model.internal.PathUtil
+
+/**
+ * @author Hans Dockter
+ */
+
+class Output implements ClasspathEntry {
+    String path
+
+    def Output(Node node) {
+        this(node. at path)
+    }
+
+    def Output(String path) {
+        assert path != null
+        this.path = PathUtil.normalizePath(path)
+    }
+
+    String getKind() {
+        'output'
+    }
+
+    void appendNode(Node node) {
+        node.appendNode('classpathentry', [kind: getKind(), path: path])
+    }
+
+    boolean equals(o) {
+        if (this.is(o)) { return true }
+
+        if (getClass() != o.class) { return false }
+
+        Output output = (Output) o;
+
+        if (path != output.path) { return false }
+
+        return true
+    }
+
+    int hashCode() {
+        return path.hashCode();
+    }
+
+    public String toString() {
+        return "Output{" +
+                "path='" + path + '\'' +
+                '}';
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Project.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Project.groovy
new file mode 100644
index 0000000..8fd1980
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Project.groovy
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.eclipse.model
+
+import org.gradle.api.internal.XmlTransformer
+import org.gradle.plugins.ide.internal.generator.XmlPersistableConfigurationObject
+
+/**
+ * Represents the customizable elements of an eclipse project file. (via XML hooks everything is customizable).
+ *
+ * @author Hans Dockter
+ */
+class Project extends XmlPersistableConfigurationObject {
+    public static final String PROJECT_FILE_NAME = ".project";
+
+    /**
+     * The name used for the name of the eclipse project
+     */
+    String name;
+
+    /**
+     * A comment used for the eclipse project
+     */
+    String comment;
+
+    /**
+     * The referenced projects of this Eclipse project.
+     */
+    Set<String> referencedProjects = new LinkedHashSet<String>()
+
+    /**
+     * The natures to be added to this Eclipse project.
+     */
+    List<String> natures = []
+
+    /**
+     * The build commands to be added to this Eclipse project.
+     */
+    List buildCommands = []
+
+    /**
+     * The linkedResources to be added to this Eclipse project.
+     */
+    Set<Link> linkedResources = new LinkedHashSet<Link>()
+
+    /**
+     * The links to be added to this Eclipse project.
+     * <p>
+     * @deprecated Please use linkedResources
+     */
+    @Deprecated
+    Set<Link> getLinks() {
+        this.linkedResources
+    }
+
+    /**
+     * @deprecated Please use linkedResources
+     */
+    @Deprecated
+    void setLinks(Set<Link> links) {
+        this.linkedResources(links)
+    }
+
+    def Project(XmlTransformer xmlTransformer) {
+        super(xmlTransformer)
+    }
+
+    @Override protected String getDefaultResourceName() {
+        return 'defaultProject.xml'
+    }
+
+    @Override protected void load(Node xml) {
+        this.name = xml.name.text()
+        this.comment = xml.comment.text()
+        readReferencedProjects()
+        readNatures()
+        readBuildCommands()
+        readLinkedResources()
+    }
+
+    private def readReferencedProjects() {
+        return xml.projects.project.each {
+            this.referencedProjects.add(it.text())
+        }
+    }
+
+    private def readNatures() {
+        return xml.natures.nature.each { this.natures.add(it.text()) }
+    }
+
+    private def readBuildCommands() {
+        return xml.buildSpec.buildCommand.each { command ->
+            Map args = [:]
+            command.arguments.dictionary.each { Node it ->
+                args[it.key.text()] = it.value.text()
+            }
+            this.buildCommands.add(new BuildCommand(command.name.text(), args))
+        }
+    }
+
+    private def readLinkedResources() {
+        return xml.linkedResources.link.each { link ->
+            this.linkedResources.add(new Link(link.name?.text(), link.type?.text(), link.location?.text(), link.locationURI?.text()))
+        }
+    }
+
+    def configure(EclipseProject eclipseProject) {
+        if (eclipseProject.name) {
+            this.name = eclipseProject.name
+        }
+        if (eclipseProject.comment) {
+            this.comment = eclipseProject.comment
+        }
+        this.referencedProjects.addAll(eclipseProject.referencedProjects)
+        this.natures.addAll(eclipseProject.natures)
+        this.natures.unique()
+        this.buildCommands.addAll(eclipseProject.buildCommands)
+        this.buildCommands.unique()
+        this.linkedResources.addAll(eclipseProject.linkedResources);
+    }
+
+    @Override protected void store(Node xml) {
+        ['name', 'comment', 'projects', 'natures', 'buildSpec', 'linkedResources'].each { childNodeName ->
+            Node childNode = xml.children().find { it.name() == childNodeName }
+            if (childNode) {
+                xml.remove(childNode)
+            }
+        }
+        xml.appendNode('name', this.name)
+        xml.appendNode('comment', this.comment ?: null)
+        addReferencedProjectsToXml()
+        addNaturesToXml()
+        addBuildSpecToXml()
+        addLinkedResourcesToXml()
+    }
+
+    private def addReferencedProjectsToXml() {
+        def referencedProjectsNode = xml.appendNode('projects')
+        this.referencedProjects.each { projectName ->
+            referencedProjectsNode.appendNode('project', projectName)
+        }
+    }
+
+    private def addNaturesToXml() {
+        def naturesNode = xml.appendNode('natures')
+        this.natures.each { nature ->
+            naturesNode.appendNode('nature', nature)
+        }
+    }
+
+    private def addBuildSpecToXml() {
+        def buildSpec = xml.appendNode('buildSpec')
+        this.buildCommands.each { command ->
+            def commandNode = buildSpec.appendNode('buildCommand')
+            commandNode.appendNode('name', command.name)
+            def argumentsNode = commandNode.appendNode('arguments')
+            command.arguments.each { key, value ->
+                def dictionaryNode = argumentsNode.appendNode('dictionary')
+                dictionaryNode.appendNode('key', key)
+                dictionaryNode.appendNode('value', value)
+            }
+        }
+    }
+
+    private def addLinkedResourcesToXml() {
+        def parent = xml.appendNode('linkedResources')
+        this.linkedResources.each { link ->
+            def linkNode = parent.appendNode('link')
+            linkNode.appendNode('name', link.name)
+            linkNode.appendNode('type', link.type)
+            if (link.location) {
+                linkNode.appendNode('location', link.location)
+            }
+            if (link.locationUri) {
+                linkNode.appendNode('locationURI', link.locationUri)
+            }
+        }
+    }
+
+    boolean equals(o) {
+        if (this.is(o)) { return true }
+
+        if (getClass() != o.class) { return false }
+
+        Project project = (Project) o;
+
+        if (buildCommands != project.buildCommands) { return false }
+        if (comment != project.comment) { return false }
+        if (linkedResources != project.linkedResources) { return false }
+        if (name != project.name) { return false }
+        if (natures != project.natures) { return false }
+        if (referencedProjects != project.referencedProjects) { return false }
+
+        return true
+    }
+
+    int hashCode() {
+        int result;
+
+        result = (name != null ? name.hashCode() : 0);
+        result = 31 * result + (comment != null ? comment.hashCode() : 0);
+        result = 31 * result + (referencedProjects != null ? referencedProjects.hashCode() : 0);
+        result = 31 * result + (natures != null ? natures.hashCode() : 0);
+        result = 31 * result + (buildCommands != null ? buildCommands.hashCode() : 0);
+        result = 31 * result + (linkedResources != null ? linkedResources.hashCode() : 0);
+        return result;
+    }
+
+
+    public String toString() {
+        return "Project{" +
+                "name='" + name + '\'' +
+                ", comment='" + comment + '\'' +
+                ", referencedProjects=" + referencedProjects +
+                ", natures=" + natures +
+                ", buildCommands=" + buildCommands +
+                ", linkedResources=" + linkedResources +
+                '}';
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/ProjectDependency.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/ProjectDependency.groovy
new file mode 100644
index 0000000..0b60866
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/ProjectDependency.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.plugins.ide.eclipse.model
+
+/**
+ * @author Hans Dockter
+ */
+class ProjectDependency extends AbstractClasspathEntry {
+
+    String gradlePath
+
+    ProjectDependency(Node node) {
+        super(node)
+        assertPathIsValid()
+    }
+
+    ProjectDependency(String path, boolean exported, String nativeLibraryLocation, Set accessRules, String gradlePath) {
+        super(path, exported, nativeLibraryLocation, accessRules)
+        assertPathIsValid()
+        this.gradlePath = gradlePath
+    }
+
+    private void assertPathIsValid() {
+        assert path.startsWith('/')
+    }
+
+    String getKind() {
+        'src'
+    }
+
+    public String toString() {
+        return "ProjectDependency{" + super.toString()
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/SourceFolder.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/SourceFolder.groovy
new file mode 100644
index 0000000..0664b9a
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/SourceFolder.groovy
@@ -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.plugins.ide.eclipse.model
+
+/**
+ * @author Hans Dockter
+ */
+class SourceFolder extends AbstractClasspathEntry {
+    String output
+    List includes
+    List excludes
+
+    SourceFolder(Node node) {
+        super(node)
+        this.output = normalizePath(node. at output)
+        this.includes = node. at including?.split('\\|') ?: []
+        this.excludes = node. at excluding?.split('\\|') ?: []
+    }
+
+    SourceFolder(String path, String nativeLibraryLocation, Set accessRules, String output,
+                     List includes, List excludes) {
+        super(path, false, nativeLibraryLocation, accessRules)
+        this.output = normalizePath(output);
+        this.includes = includes ?: [];
+        this.excludes = excludes ?: [];
+    }
+
+    String getKind() {
+        'src'
+    }
+
+    void appendNode(Node node) {
+        addClasspathEntry(node, [including: includes.join("|"), excluding: excludes.join("|"), output: output])
+    }
+
+    boolean equals(o) {
+        if (this.is(o)) { return true }
+
+        if (getClass() != o.class) { return false }
+
+        SourceFolder that = (SourceFolder) o;
+
+        if (exported != that.exported) { return false }
+        if (accessRules != that.accessRules) { return false }
+        if (excludes != that.excludes) { return false }
+        if (includes != that.includes) { return false }
+        if (nativeLibraryLocation != that.nativeLibraryLocation) { return false }
+        if (output != that.output) { return false }
+        if (path != that.path) { return false }
+
+        return true
+    }
+
+    int hashCode() {
+        int result;
+
+        result = path.hashCode();
+        result = 31 * result + (nativeLibraryLocation != null ? nativeLibraryLocation.hashCode() : 0);
+        result = 31 * result + (exported ? 1 : 0);
+        result = 31 * result + accessRules.hashCode();
+        result = 31 * result + (output != null ? output.hashCode() : 0);
+        result = 31 * result + excludes.hashCode();
+        result = 31 * result + includes.hashCode();
+        return result;
+    }
+
+    public String toString() {
+        return "SourceFolder{" +
+                "path='" + path + '\'' +
+                ", nativeLibraryLocation='" + nativeLibraryLocation + '\'' +
+                ", exported=" + exported +
+                ", accessRules=" + accessRules +
+                ", output='" + output + '\'' +
+                ", excludes=" + excludes +
+                ", includes=" + includes +
+                '}';
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Variable.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Variable.groovy
new file mode 100644
index 0000000..6128bb2
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Variable.groovy
@@ -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.plugins.ide.eclipse.model
+
+/**
+ * @author Hans Dockter
+ */
+class Variable extends AbstractLibrary {
+    Variable(Node node) {
+        super(node)
+    }
+
+    Variable(String path, boolean exported, String nativeLibraryLocation, Set accessRules, String sourcePath, String javadocPath) {
+        super(path, exported, nativeLibraryLocation, accessRules, sourcePath, javadocPath)
+    }
+
+    String getKind() {
+        'var'
+    }
+
+    public String toString() {
+        return "Variable{" + super.toString()
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/WbDependentModule.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/WbDependentModule.groovy
new file mode 100644
index 0000000..48077b6
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/WbDependentModule.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.ide.eclipse.model
+
+import org.gradle.plugins.ide.eclipse.model.internal.PathUtil
+
+/**
+ * @author Hans Dockter
+ */
+
+class WbDependentModule {
+    String deployPath
+    String handle
+
+    def WbDependentModule(node) {
+        this(node.@'deploy-path', node. at handle)
+    }
+
+    def WbDependentModule(String deployPath, String handle) {
+        assert deployPath != null && handle != null
+        this.deployPath = PathUtil.normalizePath(deployPath)
+        this.handle = handle
+    }
+
+    void appendNode(Node parentNode) {
+        Node node = parentNode.appendNode("dependent-module", ['deploy-path': deployPath, 'handle': handle])
+        node.appendNode('dependency-type').setValue('uses')
+    }
+
+    boolean equals(o) {
+        if (this.is(o)) { return true }
+
+        if (getClass() != o.class) { return false }
+
+        WbDependentModule that = (WbDependentModule) o;
+
+        if (deployPath != that.deployPath) { return false }
+        if (handle != that.handle) { return false }
+
+        return true
+    }
+
+    int hashCode() {
+        int result;
+
+        result = deployPath.hashCode();
+        result = 31 * result + handle.hashCode();
+        return result;
+    }
+
+    public String toString() {
+        return "WbDependentModule{" +
+                "deployPath='" + deployPath + '\'' +
+                ", handle='" + handle + '\'' +
+                '}';
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/WbProperty.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/WbProperty.groovy
new file mode 100644
index 0000000..8a44f01
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/WbProperty.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.plugins.ide.eclipse.model
+
+/**
+ * @author Hans Dockter
+ */
+
+class WbProperty {
+    String name
+    String value
+
+    def WbProperty(node) {
+        this(node. at name, node. at value)
+    }
+
+    def WbProperty(String name, String value) {
+        assert name != null && value != null
+        this.name = name
+        this.value = value
+    }
+
+    void appendNode(Node node) {
+        node.appendNode("property", [name: name, value: value])
+    }
+
+    boolean equals(o) {
+        if (this.is(o)) { return true }
+
+        if (getClass() != o.class) { return false }
+
+        WbProperty that = (WbProperty) o;
+
+        if (name != that.name) { return false }
+        if (value != that.value) { return false }
+
+        return true
+    }
+
+    int hashCode() {
+        int result;
+
+        result = name.hashCode();
+        result = 31 * result + value.hashCode();
+        return result;
+    }
+
+    public String toString() {
+        return "WbProperty{" +
+                "name='" + name + '\'' +
+                ", value='" + value + '\'' +
+                '}';
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/WbResource.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/WbResource.groovy
new file mode 100644
index 0000000..baa3ced
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/WbResource.groovy
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.eclipse.model
+
+import org.gradle.plugins.ide.eclipse.model.internal.PathUtil
+
+/**
+ * @author Hans Dockter
+ */
+
+class WbResource {
+    String deployPath
+    String sourcePath
+
+    def WbResource(node) {
+        this(node.@'deploy-path', node.@'source-path')
+    }
+
+    def WbResource(String deployPath, String sourcePath) {
+        assert deployPath != null && sourcePath != null
+        this.deployPath = PathUtil.normalizePath(deployPath)
+        this.sourcePath = PathUtil.normalizePath(sourcePath)
+    }
+
+    void appendNode(Node node) {
+        node.appendNode("wb-resource", ['deploy-path': deployPath, 'source-path': sourcePath])
+    }
+
+    boolean equals(o) {
+        if (this.is(o)) { return true }
+
+        if (getClass() != o.class) { return false }
+
+        WbResource that = (WbResource) o;
+
+        if (deployPath != that.deployPath) { return false }
+        if (sourcePath != that.sourcePath) { return false }
+
+        return true
+    }
+
+    int hashCode() {
+        int result;
+
+        result = deployPath.hashCode();
+        result = 31 * result + sourcePath.hashCode();
+        return result;
+    }
+
+    public String toString() {
+        return "WbResource{" +
+                "deployPath='" + deployPath + '\'' +
+                ", sourcePath='" + sourcePath + '\'' +
+                '}';
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/WtpComponent.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/WtpComponent.groovy
new file mode 100644
index 0000000..2d835df
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/WtpComponent.groovy
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.eclipse.model
+
+import org.gradle.api.internal.XmlTransformer
+import org.gradle.plugins.ide.internal.generator.XmlPersistableConfigurationObject
+
+/**
+ * Creates the .settings/org.eclipse.wst.common.component file for WTP projects.
+ *
+ * @author Hans Dockter
+ */
+class WtpComponent extends XmlPersistableConfigurationObject {
+    String deployName
+
+    String contextPath
+
+    List wbModuleEntries = [] // TODO: change to Set? introduce common base class?
+
+    WtpComponent(XmlTransformer xmlTransformer) {
+        super(xmlTransformer)
+    }
+
+    @Override protected void load(Node xml) {
+        deployName = xml.'wb-module'[0].@'deploy-name'
+
+        xml.'wb-module'[0].children().each { node ->
+            switch (node.name()) {
+                case 'property':
+                    if (node. at name == 'context-root') {
+                        contextPath = node. at value
+                    } else {
+                        wbModuleEntries << new WbProperty(node)
+                    }
+                    break
+                case 'wb-resource':
+                    wbModuleEntries << new WbResource(node)
+                    break
+                case 'dependent-module':
+                    wbModuleEntries << new WbDependentModule(node)
+                    break
+            }
+        }
+    }
+
+    @Override protected void store(Node xml) {
+        removeConfigurableDataFromXml()
+
+        xml.'wb-module'[0].@'deploy-name' = deployName
+        if (contextPath) {
+            new WbProperty('context-root', contextPath).appendNode(xml.'wb-module')
+        }
+        wbModuleEntries.each { it.appendNode(xml.'wb-module') }
+    }
+
+    @Override protected String getDefaultResourceName() {
+        "defaultWtpComponent.xml"
+    }
+
+    void configure(String deployName, String contextPath, List wbModuleEntries) {
+        this.wbModuleEntries.addAll(wbModuleEntries)
+        this.wbModuleEntries.unique()
+        if (deployName) {
+            this.deployName = deployName
+        }
+        if (contextPath) {
+            this.contextPath = contextPath
+        }
+    }
+
+    private void removeConfigurableDataFromXml() {
+        ['property', 'wb-resource', 'dependent-module'].each { elementName ->
+            xml.'wb-module'."$elementName".each { elementNode ->
+                xml.'wb-module'[0].remove(elementNode)
+            }
+        }
+    }
+
+    boolean equals(o) {
+        if (this.is(o)) { return true }
+
+        if (getClass() != o.class) { return false }
+
+        WtpComponent wtp = (WtpComponent) o;
+
+        if (deployName != wtp.deployName) { return false }
+        if (contextPath != wtp.contextPath) { return false }
+        if (wbModuleEntries != wtp.wbModuleEntries) { return false }
+
+        return true
+    }
+
+    int hashCode() {
+        int result
+
+        result = wbModuleEntries.hashCode()
+        result = 31 * result + deployName.hashCode()
+        return result
+    }
+
+    String toString() {
+        return "WtpComponent{" +
+                "wbModuleEntries=" + wbModuleEntries +
+                ", deployName='" + deployName + '\'' +
+                ", contextPath='" + contextPath + '\'' +
+                '}'
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/WtpFacet.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/WtpFacet.groovy
new file mode 100644
index 0000000..e202cb9
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/WtpFacet.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.plugins.ide.eclipse.model
+
+import org.gradle.api.internal.XmlTransformer
+import org.gradle.plugins.ide.internal.generator.XmlPersistableConfigurationObject
+
+/**
+ * Creates the .settings/org.eclipse.wst.common.project.facet.core.xml file for WTP projects.
+ * Only handles installed facets. Fixed facets will be left untouched.
+ *
+ * @author Hans Dockter
+ */
+class WtpFacet extends XmlPersistableConfigurationObject {
+    List facets = [] // TODO: turn into Set?
+
+    WtpFacet(XmlTransformer xmlTransformer) {
+        super(xmlTransformer)
+    }
+
+    @Override protected void load(Node xml) {
+        xml.installed.each { facets.add(new Facet(it)) }
+    }
+
+    @Override protected void store(Node xml) {
+        removeConfigurableDataFromXml()
+
+        facets.each { it.appendNode(xml) }
+    }
+
+    @Override protected String getDefaultResourceName() {
+        "defaultWtpFacet.xml"
+    }
+
+    void configure(List<Facet> facets) {
+        this.facets.addAll(facets)
+        this.facets.unique()
+    }
+
+    private void removeConfigurableDataFromXml() {
+        xml.installed.each { xml.remove(it) }
+    }
+
+    boolean equals(o) {
+        if (this.is(o)) { return true }
+
+        if (getClass() != o.class) { return false }
+
+        WtpFacet wtp = (WtpFacet) o
+
+        if (facets != wtp.facets) { return false }
+
+        return true
+    }
+
+    int hashCode() {
+        facets.hashCode()
+    }
+
+    String toString() {
+        return "WtpFacet{" +
+                ", facets=" + facets +
+                '}'
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/internal/ClasspathFactory.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/internal/ClasspathFactory.groovy
new file mode 100644
index 0000000..e8e9a15
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/internal/ClasspathFactory.groovy
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.eclipse.model.internal
+
+import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
+import org.gradle.api.specs.Spec
+import org.gradle.api.tasks.SourceSet
+import org.gradle.api.artifacts.*
+import org.gradle.plugins.ide.eclipse.model.*
+import org.gradle.api.file.DirectoryTree
+
+/**
+ * @author Hans Dockter
+ */
+class ClasspathFactory {
+
+    List<ClasspathEntry> createEntries(EclipseClasspath classpath) {
+        def entries = []
+        entries.add(new Output(classpath.project.relativePath(classpath.classesOutputDir)))
+        entries.addAll(getEntriesFromSourceSets(classpath.sourceSets, classpath.project))
+        entries.addAll(getEntriesFromContainers(classpath.getContainers()))
+        entries.addAll(getEntriesFromConfigurations(classpath))
+        return entries
+    }
+
+    private List<SourceFolder> getEntriesFromSourceSets(Iterable<SourceSet> sourceSets, org.gradle.api.Project project) {
+        def entries = []
+        def sortedSourceSets = sortSourceSetsAsPerUsualConvention(sourceSets.collect{it})
+
+        sortedSourceSets.each { SourceSet sourceSet ->
+            def sortedSourceDirs = sortSourceDirsAsPerUsualConvention(sourceSet.allSource.srcDirTrees)
+
+            sortedSourceDirs.each { tree ->
+                def dir = tree.dir
+                if (dir.isDirectory()) {
+                    entries.add(new SourceFolder(
+                            project.relativePath(dir),
+                            null,
+                            [] as Set,
+                            null,
+                            tree.patterns.includes as List,
+                            tree.patterns.excludes as List))
+                }
+            }
+        }
+        entries
+    }
+
+    private List getEntriesFromContainers(Set containers) {
+        containers.collect { container ->
+            new Container(container, true, null, [] as Set)
+        }
+    }
+
+    private List getEntriesFromConfigurations(EclipseClasspath classpath) {
+        getModules(classpath) + getLibraries(classpath)
+    }
+
+    protected List getModules(EclipseClasspath classpath) {
+        return getDependencies(classpath.plusConfigurations, classpath.minusConfigurations, { it instanceof org.gradle.api.artifacts.ProjectDependency })
+                .collect { projectDependency -> new ProjectDependencyBuilder().build(projectDependency.dependencyProject) }
+    }
+
+    protected Set getLibraries(EclipseClasspath classpath) {
+        def allResolvedDependencies = resolveDependencies(classpath.plusConfigurations, classpath.minusConfigurations)
+
+        Set sourceDependencies = getResolvableDependenciesForAllResolvedDependencies(allResolvedDependencies) { dependency ->
+            addSourceArtifact(dependency)
+        }
+        Map sourceFiles = classpath.downloadSources ? getFiles(classpath.project, sourceDependencies, "sources") : [:]
+
+        Set javadocDependencies = getResolvableDependenciesForAllResolvedDependencies(allResolvedDependencies) { dependency ->
+            addJavadocArtifact(dependency)
+        }
+        Map javadocFiles = classpath.downloadJavadoc ? getFiles(classpath.project, javadocDependencies, "javadoc") : [:]
+
+        List moduleLibraries = resolveFiles(classpath.plusConfigurations, classpath.minusConfigurations).collect { File binaryFile ->
+            File sourceFile = sourceFiles[binaryFile.name]
+            File javadocFile = javadocFiles[binaryFile.name]
+            createLibraryEntry(binaryFile, sourceFile, javadocFile, classpath.pathVariables)
+        }
+        moduleLibraries.addAll(getSelfResolvingFiles(getDependencies(classpath.plusConfigurations, classpath.minusConfigurations,
+                { it instanceof SelfResolvingDependency && !(it instanceof org.gradle.api.artifacts.ProjectDependency)}), classpath.pathVariables))
+        moduleLibraries
+    }
+
+    private getSelfResolvingFiles(Collection dependencies, Map<String, File> pathVariables) {
+        dependencies.collect { SelfResolvingDependency dependency ->
+            dependency.resolve().collect { File file ->
+                createLibraryEntry(file, null, null, pathVariables)
+            }
+        }.flatten()
+    }
+
+    AbstractLibrary createLibraryEntry(File binary, File source, File javadoc, Map<String, File> pathVariables) {
+        def usedVariableEntry = pathVariables.find { String name, File value -> binary.canonicalPath.startsWith(value.canonicalPath) }
+        if (usedVariableEntry) {
+            String name = usedVariableEntry.key
+            String value = usedVariableEntry.value.canonicalPath
+            String binaryPath = name + binary.canonicalPath.substring(value.length())
+            String sourcePath = source ? name + source.canonicalPath.substring(value.length()) : null
+            String javadocPath = javadoc ? name + javadoc.canonicalPath.substring(value.length()) : null
+            return new Variable(binaryPath, true, null, [] as Set, sourcePath, javadocPath)
+        }
+        new Library(binary.canonicalPath, true, null, [] as Set, source ? source.canonicalPath : null, javadoc ? javadoc.canonicalPath : null)
+    }
+
+    private Set<Dependency> getDependencies(Collection<Configuration> plusConfigurations, Collection<Configuration> minusConfigurations, Closure filter) {
+        def result = new LinkedHashSet()
+        for (plusConfiguration in plusConfigurations) {
+            result.addAll(plusConfiguration.allDependencies.findAll(filter))
+        }
+        for (minusConfiguration in minusConfigurations) {
+            result.removeAll(minusConfiguration.allDependencies.findAll(filter))
+        }
+        result
+    }
+
+    private Set<ResolvedDependency> resolveDependencies(Collection<Configuration> plusConfigurations, Collection<Configuration> minusConfigurations) {
+        def result = new LinkedHashSet()
+        for (plusConfiguration in plusConfigurations) {
+            result.addAll(getAllDeps(plusConfiguration.resolvedConfiguration.getFirstLevelModuleDependencies({ it instanceof ExternalDependency } as Spec)))
+        }
+        for (minusConfiguration in minusConfigurations) {
+            result.removeAll(getAllDeps(minusConfiguration.resolvedConfiguration.getFirstLevelModuleDependencies({ it instanceof ExternalDependency } as Spec)))
+        }
+        result
+    }
+
+    private Set<File> resolveFiles(Collection<Configuration> plusConfigurations, Collection<Configuration> minusConfigurations) {
+        def result = new LinkedHashSet()
+        for (plusConfiguration in plusConfigurations) {
+            result.addAll(plusConfiguration.files { it instanceof ExternalDependency })
+        }
+        for (minusConfiguration in minusConfigurations) {
+            result.removeAll(minusConfiguration.files { it instanceof ExternalDependency })
+        }
+        result
+    }
+
+    private getFiles(org.gradle.api.Project project, Set dependencies, String classifier) {
+        return project.configurations.detachedConfiguration((dependencies as Dependency[])).files.inject([:]) { result, sourceFile ->
+            String key = sourceFile.name.replace("-${classifier}.jar", '.jar')
+            result[key] = sourceFile
+            result
+        }
+    }
+
+    private List getResolvableDependenciesForAllResolvedDependencies(Set allResolvedDependencies, Closure configureClosure) {
+        return allResolvedDependencies.collect { ResolvedDependency resolvedDependency ->
+            def dependency = new DefaultExternalModuleDependency(resolvedDependency.moduleGroup, resolvedDependency.moduleName, resolvedDependency.moduleVersion,
+                    resolvedDependency.configuration)
+            dependency.transitive = false
+            configureClosure.call(dependency)
+            dependency
+        }
+    }
+
+    private List<SourceSet> sortSourceSetsAsPerUsualConvention(Collection<SourceSet> sourceSets) {
+        return sourceSets.sort { sourceSet ->
+            switch(sourceSet.name) {
+                case SourceSet.MAIN_SOURCE_SET_NAME: return 0
+                case SourceSet.TEST_SOURCE_SET_NAME: return 1
+                default: return 2
+            }
+        }
+    }
+
+    private List<DirectoryTree> sortSourceDirsAsPerUsualConvention(Collection<DirectoryTree> sourceDirs) {
+        return sourceDirs.sort { sourceDir ->
+            if (sourceDir.dir.path.endsWith("java")) { 0 }
+            else if (sourceDir.dir.path.endsWith("resources")) { 2 }
+            else { 1 }
+        }
+    }
+
+    protected Set getAllDeps(Collection deps, Set allDeps = []) {
+        deps.each { ResolvedDependency resolvedDependency ->
+            def notSeenBefore = allDeps.add(resolvedDependency)
+            if (notSeenBefore) { // defend against circular dependencies
+                getAllDeps(resolvedDependency.children, allDeps)
+            }
+        }
+        allDeps
+    }
+
+    protected void addSourceArtifact(DefaultExternalModuleDependency dependency) {
+        dependency.artifact { artifact ->
+            artifact.name = dependency.name
+            artifact.type = 'source'
+            artifact.extension = 'jar'
+            artifact.classifier = 'sources'
+        }
+    }
+
+    protected void addJavadocArtifact(DefaultExternalModuleDependency dependency) {
+        dependency.artifact { artifact ->
+            artifact.name = dependency.name
+            artifact.type = 'javadoc'
+            artifact.extension = 'jar'
+            artifact.classifier = 'javadoc'
+        }
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/internal/PathUtil.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/internal/PathUtil.groovy
new file mode 100644
index 0000000..edccbb3
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/internal/PathUtil.groovy
@@ -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.plugins.ide.eclipse.model.internal
+
+import org.apache.commons.io.FilenameUtils
+
+/**
+ * @author Hans Dockter
+ */
+class PathUtil {
+  static String normalizePath(String path) {
+        FilenameUtils.separatorsToUnix(path)
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/internal/ProjectDependencyBuilder.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/internal/ProjectDependencyBuilder.groovy
new file mode 100644
index 0000000..2a634a1
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/internal/ProjectDependencyBuilder.groovy
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.eclipse.model.internal
+
+import org.gradle.plugins.ide.eclipse.model.ProjectDependency
+
+/**
+ * @author Szczepan Faber, @date: 11.03.11
+ */
+class ProjectDependencyBuilder {
+    ProjectDependency build(gradleProject) {
+        def name
+        if (gradleProject.hasProperty('eclipseProject') && gradleProject.eclipseProject) {
+            name = gradleProject.eclipseProject.projectName
+        } else {
+            name = gradleProject.name
+        }
+        new ProjectDependency('/' + name, true, null, [] as Set, gradleProject.path)
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/internal/WtpComponentFactory.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/internal/WtpComponentFactory.groovy
new file mode 100644
index 0000000..93676d3
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/internal/WtpComponentFactory.groovy
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.eclipse.model.internal
+
+import org.apache.commons.io.FilenameUtils
+import org.gradle.api.artifacts.Dependency
+import org.gradle.api.artifacts.ExternalDependency
+import org.gradle.api.artifacts.SelfResolvingDependency
+import org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent
+import org.gradle.plugins.ide.eclipse.model.WbDependentModule
+import org.gradle.plugins.ide.eclipse.model.WbResource
+import org.gradle.plugins.ide.eclipse.model.WtpComponent
+
+/**
+ * @author Hans Dockter
+ */
+class WtpComponentFactory {
+    void configure(EclipseWtpComponent wtp, WtpComponent component) {
+        def entries = getEntriesFromSourceDirs(wtp)
+        entries.addAll(wtp.resources)
+        entries.addAll(wtp.properties)
+        entries.addAll(getEntriesFromConfigurations(wtp))
+
+        component.configure(wtp.deployName, wtp.contextPath, entries)
+    }
+
+    private List getEntriesFromSourceDirs(EclipseWtpComponent wtp) {
+        wtp.sourceDirs.findAll { it.isDirectory() }.collect { dir ->
+            new WbResource("/WEB-INF/classes", wtp.project.relativePath(dir))
+        }
+    }
+
+    private List getEntriesFromConfigurations(EclipseWtpComponent wtp) {
+        (getEntriesFromProjectDependencies(wtp) as List) + (getEntriesFromLibraries(wtp) as List)
+    }
+
+    // must include transitive project dependencies
+    private Set getEntriesFromProjectDependencies(EclipseWtpComponent wtp) {
+        def dependencies = getDependencies(wtp.plusConfigurations, wtp.minusConfigurations,
+                { it instanceof org.gradle.api.artifacts.ProjectDependency })
+
+        def projects = dependencies*.dependencyProject
+
+        def allProjects = [] as LinkedHashSet
+        allProjects.addAll(projects)
+        projects.each { collectDependedUponProjects(it, allProjects) }
+
+        allProjects.collect { project ->
+            new WbDependentModule("/WEB-INF/lib", "module:/resource/" + project.name + "/" + project.name)
+        }
+    }
+
+    // TODO: might have to search all class paths of all source sets for project dependendencies, not just runtime configuration
+    private void collectDependedUponProjects(org.gradle.api.Project project, LinkedHashSet result) {
+        def runtimeConfig = project.configurations.findByName("runtime")
+        if (runtimeConfig) {
+            def projectDeps = runtimeConfig.getAllDependencies(org.gradle.api.artifacts.ProjectDependency)
+            def dependedUponProjects = projectDeps*.dependencyProject
+            result.addAll(dependedUponProjects)
+            for (dependedUponProject in dependedUponProjects) {
+                collectDependedUponProjects(dependedUponProject, result)
+            }
+        }
+    }
+
+    // must NOT include transitive library dependencies
+    private Set getEntriesFromLibraries(EclipseWtpComponent wtp) {
+        Set declaredDependencies = getDependencies(wtp.plusConfigurations, wtp.minusConfigurations,
+                { it instanceof ExternalDependency})
+
+        Set libFiles = wtp.project.configurations.detachedConfiguration((declaredDependencies as Dependency[])).files +
+                getSelfResolvingFiles(getDependencies(wtp.plusConfigurations, wtp.minusConfigurations,
+                        { it instanceof SelfResolvingDependency && !(it instanceof org.gradle.api.artifacts.ProjectDependency)}))
+
+        libFiles.collect { file ->
+            createWbDependentModuleEntry(file, wtp.pathVariables)
+        }
+    }
+
+    private LinkedHashSet getSelfResolvingFiles(LinkedHashSet<SelfResolvingDependency> dependencies) {
+        dependencies.collect { it.resolve() }.flatten()
+    }
+
+    private WbDependentModule createWbDependentModuleEntry(File file, Map<String, File> variables) {
+        def usedVariableEntry = variables.find { name, value -> file.canonicalPath.startsWith(value.canonicalPath) }
+        def handleSnippet
+        if (usedVariableEntry) {
+            handleSnippet = "var/$usedVariableEntry.key/${file.canonicalPath.substring(usedVariableEntry.value.canonicalPath.length())}"
+        } else {
+            handleSnippet = "lib/${file.canonicalPath}"
+        }
+        handleSnippet = FilenameUtils.separatorsToUnix(handleSnippet)
+        return new WbDependentModule('/WEB-INF/lib', "module:/classpath/$handleSnippet")
+    }
+
+    private LinkedHashSet getDependencies(Set plusConfigurations, Set minusConfigurations, Closure filter) {
+        def declaredDependencies = new LinkedHashSet()
+        plusConfigurations.each { configuration ->
+            declaredDependencies.addAll(configuration.allDependencies.findAll(filter))
+        }
+        minusConfigurations.each { configuration ->
+            declaredDependencies.removeAll(configuration.allDependencies.findAll(filter))
+        }
+        return declaredDependencies
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/package-info.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/package-info.java
new file mode 100644
index 0000000..c91dcea
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/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.ide.eclipse.EclipsePlugin}.
+ */
+package org.gradle.plugins.ide.eclipse.model;
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/package-info.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/package-info.java
new file mode 100644
index 0000000..6ef9b53
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * A {@link org.gradle.api.Plugin} for generating Eclipse files.
+ */
+package org.gradle.plugins.ide.eclipse;
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/GenerateIdeaModule.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/GenerateIdeaModule.groovy
new file mode 100644
index 0000000..2e05677
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/GenerateIdeaModule.groovy
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.idea
+
+import org.gradle.api.artifacts.Configuration
+import org.gradle.plugins.ide.api.XmlGeneratorTask
+import org.gradle.plugins.ide.idea.model.IdeaModule
+import org.gradle.plugins.ide.idea.model.Module
+
+/**
+ * Generates an IDEA module file.
+ * <p>
+ * Example how to use scopes property to enable 'provided' dependencies in the output *.iml file:
+ * <pre autoTested=''>
+ * apply plugin: 'java'
+ * apply plugin: 'idea'
+ *
+ * configurations {
+ *   provided
+ *   provided.extendsFrom(compile)
+ * }
+ *
+ * dependencies {
+ *   //provided "some.interesting:dependency:1.0"
+ * }
+ *
+ * ideaModule {
+ *   scopes.PROVIDED.plus += configurations.provided
+ * }
+ * </pre>
+ *
+ * @author Hans Dockter
+ */
+public class GenerateIdeaModule extends XmlGeneratorTask<Module> {
+
+    /**
+     * Idea module model
+     */
+    IdeaModule module
+
+    //TODO SF: IMPORTANT
+    //Once we decide to break backwards compatibility below hacky delegating getters/setters will be gone
+    //and the implementation of this task will dwindle into few lines of code or disappear completely
+
+    @Override protected Module create() {
+        new Module(xmlTransformer, module.pathFactory)
+    }
+
+    @Override protected void configure(Module xmlModule) {
+        getModule().mergeXmlModule(xmlModule)
+    }
+
+    /**
+     * The content root directory of the module.
+     */
+    File getModuleDir() {
+        module.contentRoot
+    }
+
+    void setModuleDir(File contentRoot) {
+        module.contentRoot = contentRoot
+    }
+
+    /**
+     * The directories containing the production sources.
+     */
+    Set<File> getSourceDirs() {
+       module.sourceDirs
+    }
+
+    void setSourceDirs(Set<File> sourceDirs) {
+       module.sourceDirs = sourceDirs
+    }
+
+    /**
+     * The directories containing the test sources.
+     */
+    Set<File> getTestSourceDirs() {
+        module.testSourceDirs
+    }
+
+    void setTestSourceDirs(Set<File> testSourceDirs) {
+        module.testSourceDirs = testSourceDirs
+    }
+
+    /**
+     * The directories to be excluded.
+     */
+    Set<File> getExcludeDirs() {
+        module.excludeDirs
+    }
+
+    void setExcludeDirs(Set<File> excludeDirs) {
+        module.excludeDirs = excludeDirs
+    }
+
+    /**
+     * If true, output directories for this module will be located below the output directory for the project;
+     * otherwise, they will be set to the directories specified by #outputDir and #testOutputDir.
+     */
+    Boolean getInheritOutputDirs() {
+        module.inheritOutputDirs
+    }
+
+    void setInheritOutputDirs(Boolean inheritOutputDirs) {
+        module.inheritOutputDirs
+    }
+
+    /**
+     * The output directory for production classes. If {@code null}, no entry will be created.
+     */
+    File getOutputDir() {
+        module.outputDir
+    }
+
+    void setOutputDir(File outputDir) {
+        module.outputDir
+    }
+
+    /**
+     * The output directory for test classes. If {@code null}, no entry will be created.
+     */
+    File getTestOutputDir() {
+        module.testOutputDir
+    }
+
+    void setTestOutputDir(File testOutputDir) {
+        module.testOutputDir
+    }
+
+    /**
+     * The JDK to use for this module. If {@code null}, the value of the existing or default ipr XML (inherited)
+     * is used. If it is set to <code>inherited</code>, the project SDK is used. Otherwise the SDK for the corresponding
+     * value of java version is used for this module
+     */
+    String getJavaVersion() {
+        module.javaVersion
+    }
+
+    void setJavaVersion(String javaVersion) {
+        module.javaVersion = javaVersion
+    }
+
+    /**
+     * Whether to download and add sources associated with the dependency jars.
+     */
+    boolean getDownloadSources() {
+        module.downloadSources
+    }
+
+    void setDownloadSources(boolean downloadSources) {
+        module.downloadSources = downloadSources
+    }
+
+    /**
+     * Whether to download and add javadoc associated with the dependency jars.
+     */
+    boolean getDownloadJavadoc() {
+        module.downloadJavadoc
+    }
+
+    void setDownloadJavadoc(boolean downloadJavadoc) {
+        module.downloadJavadoc = downloadJavadoc
+    }
+
+    /**
+     * The variables to be used for replacing absolute paths in the iml entries. For example, you might add a
+     * {@code GRADLE_USER_HOME} variable to point to the Gradle user home dir.
+     */
+    Map<String, File> getVariables() {
+        module.variables
+    }
+
+    void setVariables(Map<String, File> variables) {
+        module.variables = variables
+    }
+
+    /**
+     * The keys of this map are the IDEA scopes. Each key points to another map that has two keys, plus and minus.
+     * The values of those keys are collections of {@link org.gradle.api.artifacts.Configuration} objects. The files of the
+     * plus configurations are added minus the files from the minus configurations. See example below...
+     * <p>
+     * Example how to use scopes property to enable 'provided' dependencies in the output *.iml file:
+     * <pre autoTested=''>
+     * apply plugin: 'java'
+     * apply plugin: 'idea'
+     *
+     * configurations {
+     *   provided
+     *   provided.extendsFrom(compile)
+     * }
+     *
+     * dependencies {
+     *   //provided "some.interesting:dependency:1.0"
+     * }
+     *
+     * ideaModule {
+     *   scopes.PROVIDED.plus += configurations.provided
+     * }
+     * </pre>
+     */
+    Map<String, Map<String, Collection<Configuration>>> getScopes() {
+        module.scopes
+    }
+
+    void setScopes(Map<String, Map<String, Collection<Configuration>>> scopes) {
+        module.scopes = scopes
+    }
+
+    /**
+     * Configures output *.iml file. It's <b>optional</b> because the task should configure it correctly for you
+     * (including making sure it is unique in the multi-module build).
+     * If you really need to change the output file name it is much easier to do it via the <b>moduleName</b> property.
+     * <p>
+     * Please refer to documentation on <b>moduleName</b> property. In IntelliJ IDEA the module name is the same as the name of the *.iml file.
+     */
+    File getOutputFile() {
+        return module.outputFile
+    }
+
+    void setOutputFile(File newOutputFile) {
+        module.outputFile = newOutputFile
+    }
+
+    /**
+     * Configures module name. It's <b>optional</b> because the task should configure it correctly for you.
+     * By default it will try to use the <b>project.name</b> or prefix it with a part of a <b>project.path</b>
+     * to make sure the moduleName is unique in the scope of a multi-module build.
+     * The 'uniqeness' of a module name is required for correct import
+     * into IntelliJ IDEA and the task will make sure the name is unique. See example below...
+     * <p>
+     * <b>moduleName</b> is a synthethic property that actually modifies the <b>outputFile</b> property value.
+     * This means that you should not configure both moduleName and outputFile at the same time. moduleName is recommended.
+     * <p>
+     * However, in case you really need to override the default moduleName this is the way to go:
+     * <pre autoTested=''>
+     * apply plugin: 'idea'
+     *
+     * ideaModule {
+     *   moduleName = 'some-important-project'
+     * }
+     * </pre>
+     * <p>
+     * <b>since</b> 1.0-milestone-2
+     */
+    String getModuleName() {
+        return module.name
+    }
+
+    void setModuleName(String moduleName) {
+        module.name = moduleName
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/GenerateIdeaProject.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/GenerateIdeaProject.groovy
new file mode 100644
index 0000000..47d9e66
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/GenerateIdeaProject.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.plugins.ide.idea
+
+import org.gradle.plugins.ide.api.XmlGeneratorTask
+import org.gradle.plugins.ide.idea.model.IdeaProject
+import org.gradle.plugins.ide.idea.model.Project
+
+/**
+ * Generates an IDEA project file.
+ *
+ * @author Hans Dockter
+ */
+public class GenerateIdeaProject extends XmlGeneratorTask<Project> {
+
+    /**
+     * idea project model
+     */
+    IdeaProject ideaProject;
+
+    @Override protected void configure(Project xmlModule) {
+        getIdeaProject().mergeXmlProject(xmlModule)
+    }
+
+    @Override Project create() {
+        def project = new Project(xmlTransformer, ideaProject.pathFactory)
+        return project
+    }
+
+    /**
+     * The subprojects that should be mapped to modules in the ipr file. The subprojects will only be mapped if the Idea plugin has been
+     * applied to them.
+     */
+    Set<org.gradle.api.Project> getSubprojects() {
+        ideaProject.subprojects
+    }
+
+    void setSubprojects(Set<org.gradle.api.Project> subprojects) {
+        ideaProject.subprojects = subprojects
+    }
+
+    /**
+     * The java version used for defining the project sdk.
+     */
+    String getJavaVersion() {
+        ideaProject.javaVersion
+    }
+
+    void setJavaVersion(String javaVersion) {
+        ideaProject.javaVersion = javaVersion
+    }
+
+    /**
+     * The wildcard resource patterns.
+     */
+    Set getWildcards() {
+        ideaProject.wildcards
+    }
+
+    void setWildcards(Set wildcards) {
+        ideaProject.wildcards = wildcards
+    }
+
+    /**
+     * output *.ipr file
+     */
+    File getOutputFile() {
+        return ideaProject.outputFile
+    }
+
+    void setOutputFile(File newOutputFile) {
+        ideaProject.outputFile = newOutputFile
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/GenerateIdeaWorkspace.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/GenerateIdeaWorkspace.groovy
new file mode 100644
index 0000000..b6ba704
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/GenerateIdeaWorkspace.groovy
@@ -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.plugins.ide.idea
+
+import org.gradle.plugins.ide.api.XmlGeneratorTask
+import org.gradle.plugins.ide.idea.model.Workspace
+
+/**
+ * Generates an IDEA workspace file.
+ *
+ * @author Hans Dockter
+ */
+public class GenerateIdeaWorkspace extends XmlGeneratorTask<Workspace> {
+
+    @Override protected Workspace create() {
+        return new Workspace(xmlTransformer)
+    }
+
+    @Override protected void configure(Workspace object) {
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/IdeaPlugin.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/IdeaPlugin.groovy
new file mode 100644
index 0000000..40dd06a
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/IdeaPlugin.groovy
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.idea;
+
+
+import org.gradle.api.JavaVersion
+import org.gradle.api.Project
+import org.gradle.api.internal.ClassGenerator
+import org.gradle.api.plugins.JavaPlugin
+import org.gradle.plugins.ide.idea.internal.IdeaNameDeduper
+import org.gradle.plugins.ide.internal.IdePlugin
+import org.gradle.plugins.ide.idea.model.*
+
+/**
+ * @author Hans Dockter
+ *
+ * Adds an IdeaModule task. When applied to a root project, also adds an IdeaProject task.
+ * For projects that have the Java plugin applied, the tasks receive additional Java-specific configuration.
+ */
+class IdeaPlugin extends IdePlugin {
+
+    IdeaModel model
+
+    @Override protected String getLifecycleTaskName() {
+        return 'idea'
+    }
+
+    @Override protected void onApply(Project project) {
+        lifecycleTask.description = 'Generates IDEA project files (IML, IPR, IWS)'
+        cleanTask.description = 'Cleans IDEA project files (IML, IPR)'
+
+        model = new IdeaModel()
+        project.convention.plugins.idea = model
+
+        configureIdeaWorkspace(project)
+        configureIdeaProject(project)
+        configureIdeaModule(project)
+        configureForJavaPlugin(project)
+
+        project.gradle.projectsEvaluated {
+            //TODO SF: is it possible to do deduplication on the fly? - same applies for eclipse
+            new IdeaNameDeduper().configure(project)
+        }
+    }
+
+    private configureIdeaWorkspace(Project project) {
+        if (isRoot(project)) {
+            def task = project.task('ideaWorkspace', description: 'Generates an IDEA workspace file (IWS)', type: GenerateIdeaWorkspace) {
+                outputFile = new File(project.projectDir, project.name + ".iws")
+            }
+            addWorker(task)
+        }
+    }
+
+    private configureIdeaModule(Project project) {
+        def task = project.task('ideaModule', description: 'Generates IDEA module files (IML)', type: GenerateIdeaModule) {
+            def iml = new IdeaModuleIml(xmlTransformer: xmlTransformer, generateTo: project.projectDir)
+            module = services.get(ClassGenerator).newInstance(IdeaModule, [project: project, iml: iml])
+
+            model.module = module
+
+            module.conventionMapping.sourceDirs = { [] as LinkedHashSet }
+            module.conventionMapping.name = { project.name }
+            module.conventionMapping.contentRoot = { project.projectDir }
+            module.conventionMapping.testSourceDirs = { [] as LinkedHashSet }
+            module.conventionMapping.excludeDirs = { [project.buildDir, project.file('.gradle')] as LinkedHashSet }
+
+            module.conventionMapping.pathFactory = {
+                PathFactory factory = new PathFactory()
+                factory.addPathVariable('MODULE_DIR', outputFile.parentFile)
+                module.variables.each { key, value ->
+                    factory.addPathVariable(key, value)
+                }
+                factory
+            }
+        }
+
+        addWorker(task)
+    }
+
+    private configureIdeaProject(Project project) {
+        if (isRoot(project)) {
+            def task = project.task('ideaProject', description: 'Generates IDEA project file (IPR)', type: GenerateIdeaProject) {
+                def ipr = new IdeaProjectIpr(xmlTransformer: xmlTransformer)
+                ideaProject = services.get(ClassGenerator).newInstance(IdeaProject, [ipr: ipr])
+
+                model.project = ideaProject
+
+                ideaProject.outputFile = new File(project.projectDir, project.name + ".ipr")
+                ideaProject.javaVersion = JavaVersion.VERSION_1_6.toString()
+                ideaProject.wildcards = ['!?*.java', '!?*.groovy'] as Set
+                ideaProject.subprojects = project.rootProject.allprojects
+                ideaProject.conventionMapping.pathFactory = {
+                    new PathFactory().addPathVariable('PROJECT_DIR', outputFile.parentFile)
+                }
+            }
+            addWorker(task)
+        }
+    }
+
+    private configureForJavaPlugin(Project project) {
+        project.plugins.withType(JavaPlugin) {
+            configureIdeaProjectForJava(project)
+            configureIdeaModuleForJava(project)
+        }
+    }
+
+    private configureIdeaProjectForJava(Project project) {
+        if (isRoot(project)) {
+            project.ideaProject {
+                javaVersion = project.sourceCompatibility
+            }
+        }
+    }
+
+    private configureIdeaModuleForJava(Project project) {
+        project.ideaModule {
+            module.conventionMapping.sourceDirs = { project.sourceSets.main.allSource.srcDirs as LinkedHashSet }
+            module.conventionMapping.testSourceDirs = { project.sourceSets.test.allSource.srcDirs as LinkedHashSet }
+            def configurations = project.configurations
+            module.scopes = [
+                    PROVIDED: [plus: [], minus: []],
+                    COMPILE: [plus: [configurations.compile], minus: []],
+                    RUNTIME: [plus: [configurations.runtime], minus: [configurations.compile]],
+                    TEST: [plus: [configurations.testRuntime], minus: [configurations.runtime]]
+            ]
+        }
+    }
+
+    private boolean isRoot(Project project) {
+        return project.parent == null
+    }
+}
+
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/internal/IdeaNameDeduper.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/internal/IdeaNameDeduper.groovy
new file mode 100644
index 0000000..14abafd
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/internal/IdeaNameDeduper.groovy
@@ -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.plugins.ide.idea.internal
+
+import org.gradle.api.Project
+import org.gradle.plugins.ide.idea.IdeaPlugin
+import org.gradle.plugins.ide.internal.configurer.DeduplicationTarget
+import org.gradle.plugins.ide.internal.configurer.ProjectDeduper
+
+/**
+ * @author Szczepan Faber, @date 03.03.11
+ */
+class IdeaNameDeduper {
+
+    void configure(Project theProject) {
+        def ideaProjects = theProject.rootProject.allprojects.findAll { it.plugins.hasPlugin(IdeaPlugin) }
+        new ProjectDeduper().dedupe(ideaProjects, { project ->
+            new DeduplicationTarget(project: project,
+                    moduleName: project.ideaModule.module.name,
+                    updateModuleName: { project.ideaModule.module.name = it } )
+        })
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Dependency.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Dependency.java
new file mode 100644
index 0000000..87442ba
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Dependency.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.plugins.ide.idea.model;
+
+import groovy.util.Node;
+
+/**
+ * Represents a dependency of an IDEA module.
+ *
+ * @author Hans Dockter
+ */
+public interface Dependency {
+    void addToNode(Node parentNode);
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaModel.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaModel.groovy
new file mode 100644
index 0000000..5e46817
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaModel.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.idea.model
+
+import org.gradle.util.ConfigureUtil
+
+/**
+ * DSL-friendly model of the IDEA project information.
+ * First point of entry when it comes to customizing the idea generation
+ * <p>
+ * See the examples in docs for {@link IdeaModule} or {@link IdeaProject}
+ * <p>
+ * Author: Szczepan Faber, created at: 3/31/11
+ */
+class IdeaModel {
+    IdeaModule module
+    IdeaProject project
+
+    void module(Closure closure) {
+        ConfigureUtil.configure(closure, getModule())
+    }
+
+    void project(Closure closure) {
+        ConfigureUtil.configure(closure, getProject())
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaModule.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaModule.groovy
new file mode 100644
index 0000000..9f51134
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaModule.groovy
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.idea.model
+
+import org.gradle.api.artifacts.Configuration
+import org.gradle.plugins.ide.idea.model.internal.IdeaDependenciesProvider
+import org.gradle.util.ConfigureUtil
+
+/**
+ * Model for an IDEA module.
+ * <p>
+ * Example of use with a blend of all possible properties.
+ * Bear in mind that usually you don't have configure this model directly because Gradle configures it for free!
+ *
+ * <pre autoTested=''>
+ * apply plugin: 'java'
+ * apply plugin: 'idea'
+ *
+ * //for the sake of the example lets have a 'provided' dependency configuration
+ * configurations {
+ *   provided
+ *   provided.extendsFrom(compile)
+ * }
+ *
+ * dependencies {
+ *   //provided "some.interesting:dependency:1.0"
+ * }
+ *
+ * idea {
+ *   module {
+ *     //if for some reason you want to add an extra sourceDirs
+ *     sourceDirs += file('some-extra-source-folder')
+ *
+ *     //and some extra test source dirs
+ *     testSourceDirs += file('some-extra-test-dir')
+ *
+ *     //and some extra dirs that should be excluded by IDEA
+ *     excludeDirs += file('some-extra-exclude-dir')
+ *
+ *     //if you don't like the name Gradle have chosen
+ *     name = 'some-better-name'
+ *
+ *     //if you prefer different output folders
+ *     inheritOutputDirs = false
+ *     outputDir = file('muchBetterOutputDir')
+ *     testOutputDir = file('muchBetterTestOutputDir')
+ *
+ *     //if you prefer different java version than inherited from IDEA project
+ *     javaVersion = '1.6'
+ *
+ *     //if you need to put provided dependencies on the classpath
+ *     scopes.PROVIDED.plus += configurations.provided
+ *
+ *     //if 'content root' (as IDEA calls it) of the module is different
+ *     contentRoot = file('my-module-content-root')
+ *
+ *     //if you love browsing javadocs
+ *     downloadJavadoc = true
+ *
+ *     //and hate reading sources :)
+ *     downloadSources = false
+ *
+ *     //if you want parts of paths in resulting *.iml to be replaced by variables (files)
+ *     variables = [GRADLE_HOME: file('~/cool-software/gradle')]
+ *     //pathVariables
+ *     //TODO SF: think about moving the pathVariables to the upper level
+ *
+ *     iml {
+ *       //if you like to keep *.iml in a secret folder
+ *       generateTo = file('secret-modules-folder')
+ *
+ *       //if you want to mess with the resulting xml in whatever way you fancy
+ *       withXml {
+ *         def node = it.asNode()
+ *         node.appendNode('iLoveGradle', 'true')
+ *         node.appendNode('butAlso', 'I find increasing pleasure tinkering with output *.iml contents. Yeah!!!')
+ *       }
+ *
+ *       //beforeMerged and whenMerged closures are the highest voodoo
+ *       //and probably should be used only to solve tricky edge cases:
+ *
+ *       //closure executed after *.iml content is loaded from existing file
+ *       //but before gradle build information is merged
+ *       beforeMerged { module ->
+ *         //if you want skip merging exclude dirs
+ *         module.excludeFolders.clear()
+ *       }
+ *
+ *       //closure executed after *.iml content is loaded from existing file
+ *       //and after gradle build information is merged
+ *       whenMerged { module ->
+ *         //If you really want to update the javaVersion
+ *         module.javaVersion = '1.6'
+ *         //but you don't want to do it here...
+ *         //because you can do it much easier in idea.module configuration!
+ *       }
+ *     }
+ *   }
+ * }
+ *
+ * </pre>
+ *
+ * Author: Szczepan Faber, created at: 3/31/11
+ */
+class IdeaModule {
+
+   /**
+     * IDEA module name; controls the name of the *.iml file
+     * <p>
+     * For example see docs for {@link IdeaModule}
+     */
+    String name
+
+    /**
+     * The directories containing the production sources.
+     * <p>
+     * For example see docs for {@link IdeaModule}
+     */
+    Set<File> sourceDirs
+
+    /**
+     * The keys of this map are the IDEA scopes. Each key points to another map that has two keys, plus and minus.
+     * The values of those keys are collections of {@link org.gradle.api.artifacts.Configuration} objects. The files of the
+     * plus configurations are added minus the files from the minus configurations. See example below...
+     * <p>
+     * Example how to use scopes property to enable 'provided' dependencies in the output *.iml file:
+     * <pre autoTested=''>
+     * apply plugin: 'java'
+     * apply plugin: 'idea'
+     *
+     * configurations {
+     *   provided
+     *   provided.extendsFrom(compile)
+     * }
+     *
+     * dependencies {
+     *   //provided "some.interesting:dependency:1.0"
+     * }
+     *
+     * idea {
+     *   module {
+     *     scopes.PROVIDED.plus += configurations.provided
+     *   }
+     * }
+     * </pre>
+     */
+    Map<String, Map<String, Collection<Configuration>>> scopes = [:]
+
+    /**
+     * Whether to download and add sources associated with the dependency jars.
+     * <p>
+     * For example see docs for {@link IdeaModule}
+     */
+    boolean downloadSources = true
+
+    /**
+     * Whether to download and add javadoc associated with the dependency jars.
+     * <p>
+     * For example see docs for {@link IdeaModule}
+     */
+    boolean downloadJavadoc = false
+
+    /**
+     * The content root directory of the module.
+     * <p>
+     * For example see docs for {@link IdeaModule}
+     */
+    File contentRoot
+
+    /**
+     * The directories containing the test sources.
+     * <p>
+     * For example see docs for {@link IdeaModule}
+     */
+    Set<File> testSourceDirs
+
+    /**
+     * The directories to be excluded.
+     * <p>
+     * Warning - it is a {@link org.gradle.api.dsl.ConvenienceProperty}
+     * <p>
+     * For example see docs for {@link IdeaModule}
+     */
+    Set<File> excludeDirs
+
+    /**
+     * If true, output directories for this module will be located below the output directory for the project;
+     * otherwise, they will be set to the directories specified by {@link #outputDir} and {@link #testOutputDir}.
+     * <p>
+     * For example see docs for {@link IdeaModule}
+     */
+    Boolean inheritOutputDirs
+
+    /**
+     * The output directory for production classes. If {@code null}, no entry will be created.
+     * <p>
+     * For example see docs for {@link IdeaModule}
+     */
+    File outputDir
+
+    /**
+     * The output directory for test classes. If {@code null}, no entry will be created.
+     * <p>
+     * For example see docs for {@link IdeaModule}
+     */
+    File testOutputDir
+
+    /**
+     * The variables to be used for replacing absolute paths in the iml entries. For example, you might add a
+     * {@code GRADLE_USER_HOME} variable to point to the Gradle user home dir.
+     * <p>
+     * For example see docs for {@link IdeaModule}
+     */
+    Map<String, File> variables = [:]
+
+    /**
+     * The JDK to use for this module. If {@code null}, the value of the existing or default ipr XML (inherited)
+     * is used. If it is set to <code>inherited</code>, the project SDK is used. Otherwise the SDK for the corresponding
+     * value of java version is used for this module
+     * <p>
+     * For example see docs for {@link IdeaModule}
+     */
+    String javaVersion = Module.INHERITED
+
+    /**
+     * Enables advanced configuration like tinkering with the output xml
+     * or affecting the way existing *.iml content is merged with gradle build information
+     * <p>
+     * For example see docs for {@link IdeaModule}
+     */
+    void iml(Closure closure) {
+        ConfigureUtil.configure(closure, getIml())
+    }
+
+    //TODO SF: most likely what's above should be a part of an interface and what's below should not be exposed.
+    //For now, below methods are protected - same applies to new model
+
+    org.gradle.api.Project project
+    Module xmlModule
+    PathFactory pathFactory
+    IdeaModuleIml iml
+
+    File getOutputFile() {
+        new File((File) iml.getGenerateTo(), getName() + ".iml")
+    }
+
+    void setOutputFile(File newOutputFile) {
+        name = newOutputFile.name.replaceFirst(/\.iml$/,"");
+        iml.generateTo = newOutputFile.parentFile
+    }
+
+    Set<Path> getSourcePaths() {
+        getSourceDirs().findAll { it.exists() }.collect { path(it) }
+    }
+
+    Set<Dependency> getDependencies() {
+        new IdeaDependenciesProvider().provide(this, getPathFactory());
+    }
+
+    Set<Path> getTestSourcePaths() {
+        getTestSourceDirs().findAll { it.exists() }.collect { getPathFactory().path(it) }
+    }
+
+    Set<Path> getExcludePaths() {
+        getExcludeDirs().collect { path(it) }
+    }
+
+    Path getOutputPath() {
+        getOutputDir() ? path(getOutputDir()) : null
+    }
+
+    Path getTestOutputPath() {
+        getTestOutputDir() ? path(getTestOutputDir()) : null
+    }
+
+    void mergeXmlModule(Module xmlModule) {
+        iml.beforeMerged.execute(xmlModule)
+        xmlModule.configure(getContentPath(), getSourcePaths(), getTestSourcePaths(), getExcludePaths(),
+                getInheritOutputDirs(), getOutputPath(), getTestOutputPath(), getDependencies(), getJavaVersion())
+        iml.whenMerged.execute(xmlModule)
+
+        this.xmlModule = xmlModule
+    }
+
+    void generate() {
+        xmlModule.store(getOutputFile())
+    }
+
+    Path getContentPath() {
+        path(getContentRoot())
+    }
+
+    Path path(File dir) {
+        getPathFactory().path(dir)
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaModuleIml.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaModuleIml.groovy
new file mode 100644
index 0000000..fa5ec29
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaModuleIml.groovy
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.idea.model
+
+import org.gradle.api.internal.XmlTransformer
+import org.gradle.listener.ActionBroadcast
+
+/**
+ * Models the generation/parsing/merging capabilities of idea module
+ * <p>
+ * For example see docs for {@link IdeaModule}
+ * <p>
+ * Author: Szczepan Faber, created at: 4/5/11
+ */
+class IdeaModuleIml {
+
+    ActionBroadcast whenMerged = new ActionBroadcast()
+    ActionBroadcast beforeMerged = new ActionBroadcast()
+    XmlTransformer xmlTransformer
+
+    /**
+     * Adds a closure to be called after *.iml content is loaded from existing file
+     * but before gradle build information is merged
+     * <p>
+     * This is advanced api that gives access to internal implementation of idea plugin.
+     * It might be useful if you want to alter the way gradle build information is merged into existing *.iml content
+     * <p>
+     * The {@link Module} object is passed as a parameter to the closure
+     * <p>
+     * For example see docs for {@link IdeaModule}
+     *
+     * @param closure The closure to execute.
+     */
+    public void beforeMerged(Closure closure) {
+        beforeMerged.add(closure)
+    }
+
+    /**
+     * Adds a closure to be called after *.iml content is loaded from existing file
+     * and after gradle build information is merged
+     * <p>
+     * This is advanced api that gives access to internal implementation of idea plugin.
+     * Use it only to tackle some tricky edge cases.
+     * <p>
+     * The {@link Module} object is passed as a parameter to the closure
+     * <p>
+     * For example see docs for {@link IdeaModule}
+     *
+     * @param closure The closure to execute.
+     */
+    public void whenMerged(Closure closure) {
+        whenMerged.add(closure)
+    }
+
+    /**
+     * Adds a closure to be called when the *.iml file 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 before
+     * it is written to the output file.
+     * <p>
+     * For example see docs for {@link IdeaModule}
+     *
+     * @param closure The closure to execute when the XML has been created.
+     */
+    public void withXml(Closure closure) {
+        xmlTransformer.addAction(closure)
+    }
+
+    /**
+     * Folder where the *.iml file will be generated to
+     * <p>
+     * For example see docs for {@link IdeaModule}
+     */
+    File generateTo
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaProject.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaProject.groovy
new file mode 100644
index 0000000..6777556
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaProject.groovy
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.idea.model
+
+import org.gradle.plugins.ide.idea.IdeaPlugin
+import org.gradle.util.ConfigureUtil
+
+/**
+ * Model for idea project.
+ * <p>
+ * Example of use with a blend of all possible properties.
+ * Bear in mind that usually you don't have configure idea module directly because Gradle configures it for free!
+ *
+ * <pre autoTested=''>
+ * apply plugin: 'java'
+ * apply plugin: 'idea'
+ *
+ * idea {
+ *   project {
+ *     //if you want to set specific java version for the idea project
+ *     javaVersion = '1.5'
+ *
+ *     //you can update the source wildcards
+ *     wildcards += '!?*.ruby'
+ *
+ *     //you can update the project list that will make the modules list in the *.ipr
+ *     //subprojects -= project(':someProjectThatWillBeExcluded')
+ *
+ *     //you can change the output file
+ *     outputFile = new File(outputFile.parentFile, 'someBetterName.ipr')
+ *
+ *     //you can apply advanced logic to the xml generation/merging
+ *     ipr {
+ *
+ *       //you can tinker with the output *.ipr file before it's written to file
+ *       withXml {
+ *         def node = it.asNode()
+ *         node.appendNode('iLove', 'tinkering with the output *.ipr file!')
+ *       }
+ *     }
+ *   }
+ * }
+ * </pre>
+ *
+ * Author: Szczepan Faber, created at: 4/4/11
+ */
+class IdeaProject {
+
+    /**
+     * The subprojects that should be mapped to modules in the ipr file. The subprojects will only be mapped if the Idea plugin has been
+     * applied to them.
+     * <p>
+     * See the examples in the docs for {@link IdeaProject}
+     */
+    Set<org.gradle.api.Project> subprojects
+
+    /**
+     * The java version used for defining the project sdk.
+     * <p>
+     * See the examples in the docs for {@link IdeaProject}
+     */
+    String javaVersion
+
+    /**
+     * The wildcard resource patterns.
+     * <p>
+     * See the examples in the docs for {@link IdeaProject}
+     */
+    Set wildcards
+
+    /**
+     * Output *.ipr
+     * <p>
+     * See the examples in the docs for {@link IdeaProject}
+     */
+    File outputFile
+
+    /**
+     * Enables advanced configuration like tinkering with the output xml
+     * or affecting the way existing *.ipr content is merged with gradle build information
+     * <p>
+     * See the examples in the docs for {@link IdeaProject}
+     */
+    public void ipr(Closure closure) {
+        ConfigureUtil.configure(closure, getIpr())
+    }
+
+    //******
+
+    Project xmlProject
+    PathFactory pathFactory
+    IdeaProjectIpr ipr
+
+    void mergeXmlProject(Project xmlProject) {
+        this.xmlProject = xmlProject
+        def modulePaths = getSubprojects().inject(new LinkedHashSet()) { result, subproject ->
+            if (subproject.plugins.hasPlugin(IdeaPlugin)) {
+                File imlFile = subproject.ideaModule.outputFile
+                result << new ModulePath(getPathFactory().relativePath('PROJECT_DIR', imlFile))
+            }
+            result
+        }
+        xmlProject.configure(modulePaths, getJavaVersion(), getWildcards())
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaProjectIpr.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaProjectIpr.groovy
new file mode 100644
index 0000000..2ff9614
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaProjectIpr.groovy
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.idea.model
+
+import org.gradle.api.internal.XmlTransformer
+
+/**
+ * Models the generation/parsing/merging capabilities of idea project
+ * <p>
+ * For example see docs for {@link IdeaProject}
+ * <p>
+ * Author: Szczepan Faber, created at: 4/5/11
+ */
+class IdeaProjectIpr {
+    XmlTransformer xmlTransformer
+
+    /**
+     * Adds a closure to be called when the *.ipr document 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 before
+     * it is written to the output file.
+     * <p>
+     * For example see docs for {@link IdeaProject}
+     *
+     * @param closure The closure to execute when the XML has been created.
+     */
+    public void withXml(Closure closure) {
+        xmlTransformer.addAction(closure);
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/JarDirectory.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/JarDirectory.groovy
new file mode 100644
index 0000000..4719078
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/JarDirectory.groovy
@@ -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.plugins.ide.idea.model
+
+/**
+ * Represents a jar directory element of an idea module library.
+ *
+ * @author Hans Dockter
+ */
+class JarDirectory {
+    /**
+     * The path of the jar directory
+     */
+    Path path
+
+    /**
+     * The value for the recursive attribute of the jar directory element.
+     */
+    boolean recursive
+
+    def JarDirectory(path, recursive) {
+        this.path = path;
+        this.recursive = recursive;
+    }
+
+    boolean equals(o) {
+        if (this.is(o)) { return true }
+
+        if (getClass() != o.class) { return false }
+
+        JarDirectory that = (JarDirectory) o;
+
+        if (recursive != that.recursive) { return false }
+        if (path != that.path) { return false }             
+
+        return true;
+    }
+
+    int hashCode() {
+        int result;
+
+        result = path.hashCode();
+        result = 31 * result + (recursive ? 1 : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "JarDirectory{" +
+                "path=" + path +
+                ", recursive=" + recursive +
+                '}';
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Jdk.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Jdk.groovy
new file mode 100644
index 0000000..88a3c75
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Jdk.groovy
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.idea.model
+
+/**
+ * Represents a the information for the project JavaSDK. This information are attributes of the ProjectRootManager
+ * element in the ipr.
+ * 
+ * @author Hans Dockter
+ */
+class Jdk {
+    boolean assertKeyword
+    boolean jdk15 = false
+    String languageLevel
+    String projectJdkName
+
+    def Jdk(String javaVersion) {
+        if (javaVersion.startsWith("1.4")) {
+            assertKeyword = true
+            jdk15 = false
+            languageLevel = 'JDK_1_4'
+        }
+        else if (javaVersion.startsWith("1.5")) {
+            assertKeyword = true
+            jdk15 = true
+            languageLevel = 'JDK_1_5'
+        }
+        else if (javaVersion >= '1.6') {
+            assertKeyword = true
+            jdk15 = true
+            languageLevel = 'JDK_1_6'
+        }
+        else {
+            assertKeyword = false
+        }
+        projectJdkName = javaVersion
+    }
+
+    def Jdk(assertKeyword, jdk15, languageLevel, projectJdkName) {
+        this.assertKeyword = assertKeyword;
+        this.jdk15 = jdk15;
+        this.languageLevel = languageLevel
+        this.projectJdkName = projectJdkName;
+    }
+
+    boolean equals(o) {
+        if (this.is(o)) { return true }
+
+        if (getClass() != o.class) { return false }
+
+        Jdk jdk = (Jdk) o;
+
+        if (assertKeyword != jdk.assertKeyword) { return false }
+        if (jdk15 != jdk.jdk15) { return false }
+        if (languageLevel != jdk.languageLevel) { return false }
+        if (projectJdkName != jdk.projectJdkName) { return false }
+
+        return true;
+    }
+
+    int hashCode() {
+        int result;
+
+        result = 31 * result + (assertKeyword ? 1 : 0);
+        result = 31 * result + (jdk15 ? 1 : 0);
+        result = 31 * result + (languageLevel != null ? languageLevel.hashCode() : 0);
+        result = 31 * result + (projectJdkName != null ? projectJdkName.hashCode() : 0);
+        return result;
+    }
+
+
+    public String toString() {
+        return "Jdk{" +
+                "assertKeyword=" + assertKeyword +
+                ", jdk15=" + jdk15 +
+                ", languageLevel='" + languageLevel +
+                "', projectJdkName='" + projectJdkName + '\'' +
+                '}';
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Module.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Module.groovy
new file mode 100644
index 0000000..8369a72
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Module.groovy
@@ -0,0 +1,333 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.idea.model
+
+import org.gradle.api.internal.XmlTransformer
+import org.gradle.plugins.ide.internal.generator.XmlPersistableConfigurationObject
+
+/**
+ * Represents the customizable elements of an iml (via XML hooks everything of the iml is customizable).
+ *
+ * @author Hans Dockter
+ */
+class Module extends XmlPersistableConfigurationObject {
+    static final String INHERITED = "inherited"
+
+    /**
+     * The directory for the content root of the module.  Defaults to the project dirctory.
+     * If null, the directory containing the output file will be used.
+     */
+    Path contentPath
+
+    /**
+     * The directories containing the production sources. Must not be null.
+     */
+    Set<Path> sourceFolders = [] as LinkedHashSet
+
+    /**
+     * The directories containing the test sources. Must not be null.
+     */
+    Set<Path> testSourceFolders = [] as LinkedHashSet
+
+    /**
+     * The directories to be excluded. Must not be null.
+     */
+    Set<Path> excludeFolders = [] as LinkedHashSet
+
+    /**
+     * If true, output directories for this module will be located below the output directory for the project;
+     * otherwise, {@link #outputDir} and {@link #testOutputDir} will take effect.
+     */
+    boolean inheritOutputDirs
+
+    /**
+     * The output directory for production classes. If {@code null}, no entry will be created.
+     */
+    Path outputDir
+
+    /**
+     * The output directory for test classes. If {@code null}, no entry will be created.
+     */
+    Path testOutputDir
+
+    /**
+     * The dependencies of this module. Must not be null.
+     */
+    Set<Dependency> dependencies = [] as LinkedHashSet
+
+    String javaVersion
+
+    private final PathFactory pathFactory
+
+    Module(XmlTransformer withXmlActions, PathFactory pathFactory) {
+        super(withXmlActions)
+        this.pathFactory = pathFactory
+    }
+
+    @Override protected String getDefaultResourceName() {
+        return 'defaultModule.xml'
+    }
+
+    @Override protected void load(Node xml) {
+        readJdkFromXml()
+        readSourceAndExcludeFolderFromXml()
+        readInheritOutputDirsFromXml()
+        readOutputDirsFromXml()
+        readDependenciesFromXml()
+    }
+
+    private readJdkFromXml() {
+        def jdk = findOrderEntries().find { it. at type == 'jdk' }
+        javaVersion = jdk ? jdk. at jdkName : INHERITED
+    }
+
+    private readSourceAndExcludeFolderFromXml() {
+        findSourceFolder().each { sourceFolder ->
+            if (sourceFolder. at isTestSource == 'false') {
+                sourceFolders.add(pathFactory.path(sourceFolder. at url))
+            } else {
+                testSourceFolders.add(pathFactory.path(sourceFolder. at url))
+            }
+        }
+        findExcludeFolder().each { excludeFolder ->
+            excludeFolders.add(pathFactory.path(excludeFolder. at url))
+        }
+    }
+
+    private readInheritOutputDirsFromXml() {
+        inheritOutputDirs = findNewModuleRootManager().@"inherit-compiler-output" == "true"
+    }
+
+    private readOutputDirsFromXml() {
+        def outputDirUrl = findOutputDir()?. at url
+        def testOutputDirUrl = findTestOutputDir()?. at url
+        outputDir = outputDirUrl ? pathFactory.path(outputDirUrl) : null
+        testOutputDir = testOutputDirUrl ? pathFactory.path(testOutputDirUrl) : null
+    }
+
+    private readDependenciesFromXml() {
+        return findOrderEntries().each { orderEntry ->
+            switch (orderEntry. at type) {
+                case "module-library":
+                    Set classes = orderEntry.library.CLASSES.root.collect {
+                        pathFactory.path(it. at url)
+                    }
+                    Set javadoc = orderEntry.library.JAVADOC.root.collect {
+                        pathFactory.path(it. at url)
+                    }
+                    Set sources = orderEntry.library.SOURCES.root.collect {
+                        pathFactory.path(it. at url)
+                    }
+                    Set jarDirectories = orderEntry.library.jarDirectory.collect { new JarDirectory(pathFactory.path(it. at url), Boolean.parseBoolean(it. at recursive)) }
+                    def moduleLibrary = new ModuleLibrary(classes, javadoc, sources, jarDirectories, orderEntry. at scope)
+                    dependencies.add(moduleLibrary)
+                    break
+                case "module":
+                    dependencies.add(new ModuleDependency(orderEntry.@'module-name', orderEntry. at scope))
+            }
+        }
+    }
+
+    protected def configure(Path contentPath, Set sourceFolders, Set testSourceFolders, Set excludeFolders, Boolean inheritOutputDirs, Path outputDir, Path testOutputDir, Set dependencies, String javaVersion) {
+        this.contentPath = contentPath
+        this.sourceFolders.addAll(sourceFolders)
+        this.testSourceFolders.addAll(testSourceFolders)
+        this.excludeFolders.addAll(excludeFolders)
+        if (inheritOutputDirs != null) {
+            this.inheritOutputDirs = inheritOutputDirs
+        }
+        if (outputDir) {
+            this.outputDir = outputDir
+        }
+        if (testOutputDir) {
+            this.testOutputDir = testOutputDir
+        }
+        this.dependencies = dependencies; // overwrite rather than append dependencies
+        if (javaVersion) {
+            this.javaVersion = javaVersion
+        }
+    }
+
+    @Override protected void store(Node xml) {
+        addJdkToXml()
+        setContentURL()
+        removeSourceAndExcludeFolderFromXml()
+        addSourceAndExcludeFolderToXml()
+        writeInheritOutputDirsToXml()
+        addOutputDirsToXml()
+
+        removeDependenciesFromXml()
+        addDependenciesToXml()
+    }
+
+    private addJdkToXml() {
+        assert javaVersion != null
+        Node moduleJdk = findOrderEntries().find { it. at type == 'jdk' }
+        if (javaVersion != INHERITED) {
+            Node inheritedJdk = findOrderEntries().find { it. at type == "inheritedJdk" }
+            if (inheritedJdk) {
+                inheritedJdk.parent().remove(inheritedJdk)
+            }
+            if (moduleJdk) {
+                findNewModuleRootManager().remove(moduleJdk)
+            }
+            findNewModuleRootManager().appendNode("orderEntry", [type: "jdk", jdkName: javaVersion, jdkType: "JavaSDK"])
+        } else if (!(findOrderEntries().find { it. at type == "inheritedJdk" })) {
+            if (moduleJdk) {
+                findNewModuleRootManager().remove(moduleJdk)
+            }
+            findNewModuleRootManager().appendNode("orderEntry", [type: "inheritedJdk"])
+        }
+    }
+
+    private setContentURL() {
+        if (contentPath != null) {
+            findContent(). at url = contentPath.url
+        }
+    }
+
+    private writeInheritOutputDirsToXml() {
+        findNewModuleRootManager().@"inherit-compiler-output" = inheritOutputDirs
+    }
+
+    private addOutputDirsToXml() {
+        if (outputDir) {
+            findOrCreateOutputDir(). at url = outputDir.url
+        }
+        if (testOutputDir) {
+            findOrCreateTestOutputDir(). at url = testOutputDir.url
+        }
+    }
+
+    private Node findOrCreateOutputDir() {
+        return findOutputDir() ?: findNewModuleRootManager().appendNode("output")
+    }
+
+    private Node findOrCreateTestOutputDir() {
+        return findTestOutputDir() ?: findNewModuleRootManager().appendNode("output-test")
+    }
+
+    private Set addDependenciesToXml() {
+        return dependencies.each { Dependency dependency ->
+            dependency.addToNode(findNewModuleRootManager())
+        }
+    }
+
+    private addSourceAndExcludeFolderToXml() {
+        sourceFolders.each { Path path ->
+            findContent().appendNode('sourceFolder', [url: path.url, isTestSource: 'false'])
+        }
+        testSourceFolders.each { Path path ->
+            findContent().appendNode('sourceFolder', [url: path.url, isTestSource: 'true'])
+        }
+        excludeFolders.each { Path path ->
+            findContent().appendNode('excludeFolder', [url: path.url])
+        }
+    }
+
+    private removeSourceAndExcludeFolderFromXml() {
+        findSourceFolder().each { sourceFolder ->
+            findContent().remove(sourceFolder)
+        }
+        findExcludeFolder().each { excludeFolder ->
+            findContent().remove(excludeFolder)
+        }
+    }
+
+    private removeDependenciesFromXml() {
+        return findOrderEntries().each { orderEntry ->
+            if (isDependencyOrderEntry(orderEntry)) {
+                findNewModuleRootManager().remove(orderEntry)
+            }
+        }
+    }
+
+    protected boolean isDependencyOrderEntry(def orderEntry) {
+        ['module-library', 'module'].contains(orderEntry. at type)
+    }
+
+    private Node findContent() {
+        findNewModuleRootManager().content[0]
+    }
+
+    private findSourceFolder() {
+        findContent().sourceFolder
+    }
+
+    private findExcludeFolder() {
+        findContent().excludeFolder
+    }
+
+    private Node findOutputDir() {
+        findNewModuleRootManager().output[0]
+    }
+
+    private Node findNewModuleRootManager() {
+        xml.component.find { it. at name == 'NewModuleRootManager'}
+    }
+
+    private Node findTestOutputDir() {
+        return findNewModuleRootManager().'output-test'[0]
+    }
+
+    private findOrderEntries() {
+        findNewModuleRootManager().orderEntry
+    }
+
+
+    boolean equals(o) {
+        if (this.is(o)) { return true }
+
+        if (getClass() != o.class) { return false }
+
+        Module module = (Module) o
+
+        if (dependencies != module.dependencies) { return false }
+        if (excludeFolders != module.excludeFolders) { return false }
+        if (outputDir != module.outputDir) { return false }
+        if (sourceFolders != module.sourceFolders) { return false }
+        if (testOutputDir != module.testOutputDir) { return false }
+        if (testSourceFolders != module.testSourceFolders) { return false }
+
+        return true
+    }
+
+    int hashCode() {
+        int result;
+
+        result = (sourceFolders != null ? sourceFolders.hashCode() : 0)
+        result = 31 * result + (testSourceFolders != null ? testSourceFolders.hashCode() : 0)
+        result = 31 * result + (excludeFolders != null ? excludeFolders.hashCode() : 0)
+        result = 31 * result + (inheritOutputDirs != null ? inheritOutputDirs.hashCode() : 0)
+        result = 31 * result + outputDir.hashCode()
+        result = 31 * result + testOutputDir.hashCode()
+        result = 31 * result + (dependencies != null ? dependencies.hashCode() : 0)
+        return result
+    }
+
+
+    String toString() {
+        return "Module{" +
+                "dependencies=" + dependencies +
+                ", sourceFolders=" + sourceFolders +
+                ", testSourceFolders=" + testSourceFolders +
+                ", excludeFolders=" + excludeFolders +
+                ", inheritOutputDirs=" + inheritOutputDirs +
+                ", outputDir=" + outputDir +
+                ", testOutputDir=" + testOutputDir +
+                '}'
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/ModuleDependency.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/ModuleDependency.groovy
new file mode 100644
index 0000000..7c56c10
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/ModuleDependency.groovy
@@ -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.plugins.ide.idea.model
+
+/**
+ * Represents an orderEntry of type module in the iml xml.
+ *
+ * @author Hans Dockter
+ */
+class ModuleDependency implements Dependency {
+    /**
+     * The name of the module the module depends on. Must not be null.
+     */
+    String name
+
+    /**
+     * The scope for this dependency. If null the scope attribute is not added.
+     */
+    String scope
+
+    boolean exported
+
+    def ModuleDependency(name, scope) {
+        this.name = name;
+        this.scope = scope;
+        this.exported = !scope || scope == 'COMPILE' || scope == 'RUNTIME'
+    }
+
+    void addToNode(Node parentNode) {
+        parentNode.appendNode('orderEntry', [type: 'module', 'module-name': name] + getAttributeMapForScopeAndExported())
+    }
+
+    private Map getAttributeMapForScopeAndExported() {
+        return (exported ? [exported: ""] : [:]) + ((scope && scope != 'COMPILE') ? [scope: scope] : [:])
+    }
+
+    boolean equals(o) {
+        if (this.is(o)) { return true }
+
+        if (getClass() != o.class) { return false }
+
+        ModuleDependency that = (ModuleDependency) o;
+
+        if (name != that.name) { return false }
+        if (!scopeEquals(scope, that.scope)) { return false }
+
+        return true;
+    }
+
+    private boolean scopeEquals(String lhs, String rhs) {
+        if (lhs == 'COMPILE') {
+            return !rhs || rhs == 'COMPILE'
+        } else if (rhs == 'COMPILE') {
+            return !lhs || lhs == 'COMPILE'
+        } else {
+            return lhs == rhs
+        }
+    }
+
+    int hashCode() {
+        int result;
+
+        result = name.hashCode();
+        result = 31 * result + getScopeHash();
+        return result;
+    }
+
+    private int getScopeHash() {
+        (scope && scope != 'COMPILE' ? scope.hashCode() : 0)
+    }
+
+    public String toString() {
+        return "ModuleDependency{" +
+                "name='" + name + '\'' +
+                ", scope='" + scope + '\'' +
+                '}';
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/ModuleLibrary.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/ModuleLibrary.groovy
new file mode 100644
index 0000000..c750be4
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/ModuleLibrary.groovy
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.idea.model
+
+/**
+ * Represents an orderEntry of type module-library in the iml xml.
+ *
+ * @author Hans Dockter
+ */
+class ModuleLibrary implements Dependency {
+    /**
+     * A set of  {@link org.gradle.plugins.ide.idea.model.Path}  instances for class libraries.
+     */
+    Set classes
+
+    /**
+     * A set of  {@link org.gradle.plugins.ide.idea.model.JarDirectory}  instances for directories containing jars.
+     */
+    Set jarDirectories
+
+    /**
+     * A set of  {@link org.gradle.plugins.ide.idea.model.Path}  instances for javadoc associated with the library elements.
+     */
+    Set javadoc
+
+    /**
+     * A set of  {@link org.gradle.plugins.ide.idea.model.Path}  instances for source code associated with the library elements.
+     */
+    Set sources
+
+    /**
+     * The scope of this dependency. If null the scope attribute is not added.
+     */
+    String scope
+
+    boolean exported
+
+    def ModuleLibrary(classes, javadoc, sources, jarDirectories, scope) {
+        this.classes = classes;
+        this.jarDirectories = jarDirectories;
+        this.javadoc = javadoc;
+        this.sources = sources;
+        this.scope = scope
+        this.exported = !scope || scope == 'COMPILE' || scope == 'RUNTIME'
+    }
+
+    void addToNode(Node parentNode) {
+        Node libraryNode = parentNode.appendNode('orderEntry', [type: 'module-library'] + getAttributeMapForScopeAndExported()).appendNode('library')
+        Node classesNode = libraryNode.appendNode('CLASSES')
+        Node javadocNode = libraryNode.appendNode('JAVADOC')
+        Node sourcesNode = libraryNode.appendNode('SOURCES')
+        classes.each { Path path ->
+            classesNode.appendNode('root', [url: path.url])
+        }
+        javadoc.each { Path path ->
+            javadocNode.appendNode('root', [url: path.url])
+        }
+        sources.each { Path path ->
+            sourcesNode.appendNode('root', [url: path.url])
+        }
+        jarDirectories.each { JarDirectory jarDirectory ->
+            libraryNode.appendNode('jarDirectory', [url: jarDirectory.path.url, recursive: jarDirectory.recursive])
+        }
+    }
+
+    private Map getAttributeMapForScopeAndExported() {
+        return (exported ? [exported: ""] : [:]) + ((scope && scope != 'COMPILE') ? [scope: scope] : [:])
+    }
+
+
+    boolean equals(o) {
+        if (this.is(o)) { return true }
+
+        if (getClass() != o.class) { return false }
+
+        ModuleLibrary that = (ModuleLibrary) o;
+
+        if (classes != that.classes) { return false }
+        if (jarDirectories != that.jarDirectories) { return false }
+        if (javadoc != that.javadoc) { return false }
+        if (!scopeEquals(scope, that.scope)) { return false }
+        if (sources != that.sources) { return false }
+
+        return true;
+    }
+
+    private boolean scopeEquals(String lhs, String rhs) {
+        if (lhs == 'COMPILE') {
+            return !rhs || rhs == 'COMPILE'
+        } else if (rhs == 'COMPILE') {
+            return !lhs || lhs == 'COMPILE'
+        } else {
+            return lhs == rhs
+        }
+    }
+
+
+    int hashCode() {
+        int result;
+
+        result = classes.hashCode();
+        result = 31 * result + jarDirectories.hashCode();
+        result = 31 * result + javadoc.hashCode();
+        result = 31 * result + sources.hashCode();
+        result = 31 * result + getScopeHash()
+        return result;
+    }
+
+    private int getScopeHash() {
+        (scope && scope != 'COMPILE' ? scope.hashCode() : 0)
+    }
+
+    public String toString() {
+        return "ModuleLibrary{" +
+                "classes=" + classes +
+                ", jarDirectories=" + jarDirectories +
+                ", javadoc=" + javadoc +
+                ", sources=" + sources +
+                ", scope='" + scope + '\'' +
+                '}';
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/ModulePath.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/ModulePath.groovy
new file mode 100644
index 0000000..cf6a5ee
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/ModulePath.groovy
@@ -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.plugins.ide.idea.model
+
+/**
+ * Represents a path in a format as used often in ipr and iml files.
+ *
+ * @author Hans Dockter
+ */
+
+class ModulePath {
+    /**
+     * The path string of this path.
+     */
+    final String filePath
+
+    final Path path
+
+    def ModulePath(Path path) {
+        this.path = path
+        filePath = path.relPath
+    }
+
+    def ModulePath(Path path, String filePath) {
+        this.path = path
+        this.filePath = filePath
+    }
+
+    String getUrl() {
+        return path.url
+    }
+    
+    boolean equals(o) {
+        if (this.is(o)) { return true }
+
+        if (o == null || getClass() != o.class) { return false }
+
+        ModulePath that = (ModulePath) o;
+        return path == that.path && filePath == that.filePath
+    }
+
+    int hashCode() {
+        return path.hashCode() ^ filePath.hashCode()
+    }
+
+    public String toString() {
+        return "ModulePath{" +
+                "path='" + path +
+                ", filePath='" + filePath + '\'' +
+                '}';
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Path.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Path.groovy
new file mode 100644
index 0000000..45c989c
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Path.groovy
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.idea.model
+
+/**
+ * Represents a path in a format as used often in ipr and iml files.
+ *
+ * @author Hans Dockter
+ */
+class Path {
+    /**
+     * The url of the path. Must not be null
+     */
+    final String url
+
+    /**
+     * The relative path of the path. Must not be null
+     */
+    final String relPath
+
+    final String canonicalUrl
+
+    Path(File rootDir, String rootDirString, File file) {
+        relPath = getRelativePath(rootDir, rootDirString, file)
+        url = relativePathToURI(relPath)
+        canonicalUrl = relativePathToURI(file.absolutePath.replace(File.separator, '/'))
+    }
+
+    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)
+        canonicalUrl = url
+    }
+
+    Path(String url) {
+        this(url, url)
+    }
+
+    Path(String url, String canonicalUrl) {
+        this.relPath = null
+        this.url = url
+        this.canonicalUrl = canonicalUrl
+    }
+
+    private static String getRelativePath(File rootDir, String rootDirString, File file) {
+        String relpath = getRelativePath(rootDir, file)
+        return relpath != null ? rootDirString + '/' + relpath : file.absolutePath.replace(File.separator, '/')
+    }
+
+    private static String relativePathToURI(String relpath) {
+        if (relpath.endsWith('.jar')) {
+            return 'jar://' + relpath + '!/';
+        } else {
+            return 'file://' + relpath;
+        }
+    }
+
+    // This gets a relative path even if neither path is an ancestor of the other.
+    // implementation taken from http://www.devx.com/tips/Tip/13737 and slighly modified
+    //@param relativeTo  the destinationFile
+    //@param fromFile    where the relative path starts
+
+    private static String getRelativePath(File relativeTo, File fromFile) {
+        return matchPathLists(getPathList(relativeTo), getPathList(fromFile))
+    }
+
+    private static List getPathList(File f) {
+        List list = []
+        File r = f.canonicalFile
+        while (r != null) {
+            File parent = r.parentFile
+            list.add(parent ? r.name : r.absolutePath)
+            r = parent
+        }
+
+        return list
+    }
+
+    private static String matchPathLists(List r, List f) {
+        StringBuilder s = new StringBuilder();
+
+        // 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--
+        }
+
+        // for each remaining level in the relativeTo path, add a ..
+        for (; i >= 0; i--) {
+            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])
+        }
+
+        return s.toString()
+    }
+
+    boolean equals(o) {
+        if (this.is(o)) { return true }
+
+        if (o == null || getClass() != o.class) { return false }
+
+        Path path = (Path) o;
+
+        if (canonicalUrl != path.canonicalUrl) { return false }
+
+        return true;
+    }
+
+    int hashCode() {
+        return canonicalUrl.hashCode();
+    }
+
+    public String toString() {
+        return "Path{" +
+                "url='" + url + '\'' +
+                ", canonicalUrl='" + canonicalUrl + '\'' +
+                '}';
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/PathFactory.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/PathFactory.groovy
new file mode 100644
index 0000000..ddcbad5
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/PathFactory.groovy
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.idea.model
+
+class PathFactory {
+    private final List<Map> variables = []
+    private final Map<String, File> varsByName = [:]
+
+    PathFactory addPathVariable(String name, File dir) {
+        variables << [name: "\$${name}\$", prefix: dir.absolutePath + File.separator, dir: dir]
+        varsByName[name] = dir
+        return this
+    }
+
+    /**
+     * Creates a path for the given file.
+     */
+    Path path(File file) {
+        createFile(file.canonicalFile)
+    }
+
+    private Path createFile(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)
+    }
+
+    /**
+     * Creates a path relative to the given path variable.
+     */
+    Path relativePath(String pathVar, File file) {
+        return new Path(varsByName[pathVar], "\$${pathVar}\$", file)
+    }
+
+    /**
+     * Creates a path for the given URL.
+     */
+    Path path(String url) {
+        String expandedUrl = url
+        for (variable in variables) {
+            expandedUrl = expandedUrl.replace(variable.name, variable.prefix)
+        }
+        if (expandedUrl.toLowerCase().startsWith('file://')) {
+            expandedUrl = toUrl('file', new File(expandedUrl.substring(7)).canonicalFile)
+        } else if (expandedUrl.toLowerCase().startsWith('jar://')) {
+            def parts = expandedUrl.substring(6).split('!')
+            if (parts.length == 2) {
+                expandedUrl = toUrl('jar', new File(parts[0]).canonicalFile) + '!' + parts[1]
+            }
+        }
+        return new Path(url, expandedUrl)
+    }
+
+    def toUrl(String scheme, File file) {
+        return scheme + '://' + file.absolutePath.replace(File.separator, '/')
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Project.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Project.groovy
new file mode 100644
index 0000000..c839d6f
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Project.groovy
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.idea.model
+
+import org.gradle.api.internal.XmlTransformer
+import org.gradle.plugins.ide.internal.generator.XmlPersistableConfigurationObject
+
+/**
+ * Represents the customizable elements of an ipr (via XML hooks everything of the ipr is customizable).
+ *
+ * @author Hans Dockter
+ */
+class Project extends XmlPersistableConfigurationObject {
+    /**
+     * A set of {@link Path} instances pointing to the modules contained in the ipr.
+     */
+    Set modulePaths = []
+
+    /**
+     * A set of wildcard string to be included/excluded from the resources.
+     */
+    Set wildcards = []
+
+    /**
+     * Represent the jdk information of the project java sdk.
+     */
+    Jdk jdk
+
+    private final PathFactory pathFactory
+
+    def Project(XmlTransformer xmlTransformer, pathFactory) {
+        super(xmlTransformer)
+        this.pathFactory = pathFactory
+    }
+
+    def configure(Set modulePaths, String javaVersion, Set wildcards) {
+        if (javaVersion) {
+            jdk = new Jdk(javaVersion)
+        }
+        this.modulePaths.addAll(modulePaths)
+        this.wildcards.addAll(wildcards)
+    }
+
+    @Override protected void load(Node xml) {
+        findModules().module.each { module ->
+            this.modulePaths.add(new ModulePath(pathFactory.path(module. at fileurl), module. at filepath))
+        }
+
+        findWildcardResourcePatterns().entry.each { entry ->
+            this.wildcards.add(entry. at name)
+        }
+        def jdkValues = findProjectRootManager().attributes()
+
+        jdk = new Jdk(Boolean.parseBoolean(jdkValues.'assert-keyword'), Boolean.parseBoolean(jdkValues.'jdk-15'),
+                jdkValues.languageLevel, jdkValues.'project-jdk-name')
+    }
+
+    @Override protected String getDefaultResourceName() {
+        return "defaultProject.xml"
+    }
+
+    @Override protected void store(Node xml) {
+        findModules().replaceNode {
+            modules {
+                modulePaths.each { ModulePath modulePath ->
+                    module(fileurl: modulePath.path.url, filepath: modulePath.filePath)
+                }
+            }
+        }
+        findWildcardResourcePatterns().replaceNode {
+            wildcardResourcePatterns {
+                this.wildcards.each { wildcard ->
+                    entry(name: wildcard)
+                }
+            }
+        }
+        findProjectRootManager().@'assert-keyword' = jdk.assertKeyword
+        findProjectRootManager().@'assert-jdk-15' = jdk.jdk15
+        findProjectRootManager(). at languageLevel = jdk.languageLevel
+        findProjectRootManager().@'project-jdk-name' = jdk.projectJdkName
+    }
+
+    private def findProjectRootManager() {
+        return xml.component.find { it. at name == 'ProjectRootManager'}
+    }
+
+    private def findWildcardResourcePatterns() {
+        xml.component.find { it. at name == 'CompilerConfiguration'}.wildcardResourcePatterns
+    }
+
+    private def findModules() {
+        def moduleManager = xml.component.find { it. at name == 'ProjectModuleManager'}
+        if (!moduleManager.modules) {
+            moduleManager.appendNode('modules')
+        }
+        moduleManager.modules
+    }
+
+    boolean equals(o) {
+        if (this.is(o)) { return true }
+
+        if (getClass() != o.class) { return false }
+
+        Project project = (Project) o;
+
+        if (jdk != project.jdk) { return false }
+        if (modulePaths != project.modulePaths) { return false }
+        if (wildcards != project.wildcards) { return false }
+
+        return true;
+    }
+
+    int hashCode() {
+        int result;
+
+        result = (modulePaths != null ? modulePaths.hashCode() : 0);
+        result = 31 * result + (wildcards != null ? wildcards.hashCode() : 0);
+        result = 31 * result + (jdk != null ? jdk.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Workspace.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Workspace.groovy
new file mode 100644
index 0000000..e6438aa
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Workspace.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.plugins.ide.idea.model
+
+import org.gradle.api.internal.XmlTransformer
+import org.gradle.plugins.ide.internal.generator.XmlPersistableConfigurationObject
+
+/**
+ * Represents the customizable elements of an ipr (via XML hooks everything of the ipr is customizable).
+ *
+ * @author Hans Dockter
+ */
+
+class Workspace extends XmlPersistableConfigurationObject {
+    def Workspace(XmlTransformer withXmlActions) {
+        super(withXmlActions)
+    }
+
+    @Override protected String getDefaultResourceName() {
+        return 'defaultWorkspace.xml'
+    }
+
+    @Override protected void load(Node xml) {
+    }
+
+    @Override protected void store(Node xml) {
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/internal/IdeaDependenciesProvider.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/internal/IdeaDependenciesProvider.groovy
new file mode 100644
index 0000000..67b728b
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/internal/IdeaDependenciesProvider.groovy
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.idea.model.internal
+
+import org.gradle.api.Project
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.artifacts.Dependency
+import org.gradle.api.artifacts.ExternalDependency
+import org.gradle.api.artifacts.ProjectDependency
+import org.gradle.api.artifacts.ResolvedDependency
+import org.gradle.api.artifacts.SelfResolvingDependency
+import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
+import org.gradle.api.specs.Spec
+import org.gradle.plugins.ide.idea.model.IdeaModule
+import org.gradle.plugins.ide.idea.model.ModuleLibrary
+import org.gradle.plugins.ide.idea.model.Path
+import org.gradle.plugins.ide.idea.model.PathFactory
+
+/**
+ * All code was refactored from the GenerateIdeaModule class.
+ * TODO SF: This class should be refactored so that we can reuse it in Eclipse plugin also.
+ *
+ * Author: Szczepan Faber, created at: 4/1/11
+ */
+class IdeaDependenciesProvider {
+
+    Project project
+    Map<String, Map<String, Collection<Configuration>>> scopes
+    boolean downloadSources
+    boolean downloadJavadoc
+    PathFactory pathFactory
+
+    Set<org.gradle.plugins.ide.idea.model.Dependency> provide(IdeaModule ideaModule, PathFactory pathFactory) {
+        //TODO SF: below assignments are funky but I wanted to make little changes to the code refactored from GenerateIdeaModule
+        this.project = ideaModule.project
+        this.scopes = ideaModule.scopes
+        this.downloadSources = ideaModule.downloadSources
+        this.downloadJavadoc = ideaModule.downloadJavadoc
+        this.pathFactory = pathFactory
+
+        scopes.keySet().inject([] as LinkedHashSet) { result, scope ->
+            result.addAll(getModuleLibraries(scope))
+            result.addAll(getModules(scope))
+            result
+        }
+    }
+
+    protected Set getModules(String scope) {
+        if (scopes[scope]) {
+            return getScopeDependencies(scopes[scope], { it instanceof ProjectDependency }).collect { ProjectDependency dependency ->
+                def project = dependency.dependencyProject
+                return new ModuleDependencyBuilder().create(project, scope)
+            }
+        }
+        return []
+    }
+
+    protected Set getModuleLibraries(String scope) {
+        if (!scopes[scope]) { return [] }
+
+        def allResolvedDependencies = resolveDependencies(scopes[scope].plus, scopes[scope].minus)
+
+        Set sourceDependencies = getResolvableDependenciesForAllResolvedDependencies(allResolvedDependencies) { dependency ->
+            addSourceArtifact(dependency)
+        }
+        Map sourceFiles = downloadSources ? getFiles(sourceDependencies, "sources") : [:]
+
+        Set javadocDependencies = getResolvableDependenciesForAllResolvedDependencies(allResolvedDependencies) { dependency ->
+            addJavadocArtifact(dependency)
+        }
+        Map javadocFiles = downloadJavadoc ? getFiles(javadocDependencies, "javadoc") : [:]
+
+        List moduleLibraries = resolveFiles(scopes[scope].plus, scopes[scope].minus).collect { File binaryFile ->
+            File sourceFile = sourceFiles[binaryFile.name]
+            File javadocFile = javadocFiles[binaryFile.name]
+            new ModuleLibrary([getPath(binaryFile)] as Set, javadocFile ? [getPath(javadocFile)] as Set : [] as Set, sourceFile ? [getPath(sourceFile)] as Set : [] as Set, [] as Set, scope)
+        }
+        moduleLibraries.addAll(getSelfResolvingFiles(getScopeDependencies(scopes[scope],
+                { it instanceof SelfResolvingDependency && !(it instanceof ProjectDependency)}), scope))
+        return moduleLibraries as LinkedHashSet
+    }
+
+    private def getSelfResolvingFiles(Collection dependencies, String scope) {
+        dependencies.inject([] as LinkedHashSet) { result, SelfResolvingDependency selfResolvingDependency ->
+            result.addAll(selfResolvingDependency.resolve().collect { File file ->
+                new ModuleLibrary([getPath(file)] as Set, [] as Set, [] as Set, [] as Set, scope)
+            })
+            result
+        }
+    }
+
+    private Set getScopeDependencies(Map<String, Collection<Configuration>> configurations, Closure filter) {
+        Set firstLevelDependencies = new LinkedHashSet()
+        configurations.plus.each { Configuration configuration ->
+            firstLevelDependencies.addAll(configuration.getAllDependencies().findAll(filter))
+        }
+        configurations.minus.each { Configuration configuration ->
+            configuration.getAllDependencies().findAll(filter).each { minusDep ->
+                // This deals with dependencies that are defined in different scopes with different
+                // artifacts. Right now we accept the fact, that in such a situation some artifacts
+                // might be duplicated in Idea (they live in different scopes then).
+                if (minusDep instanceof ExternalDependency) {
+                    ExternalDependency removeCandidate = firstLevelDependencies.find { it == minusDep }
+                    if (removeCandidate && removeCandidate.artifacts == minusDep.artifacts) {
+                        firstLevelDependencies.remove(removeCandidate)
+                    }
+                } else {
+                    firstLevelDependencies.remove(minusDep)
+                }
+            }
+        }
+        return firstLevelDependencies
+    }
+
+    private Set<ResolvedDependency> resolveDependencies(Collection<Configuration> plusConfigurations, Collection<Configuration> minusConfigurations) {
+        def result = new LinkedHashSet()
+        for (plusConfiguration in plusConfigurations) {
+            result.addAll(getAllDeps(plusConfiguration.resolvedConfiguration.getFirstLevelModuleDependencies({ it instanceof ExternalDependency } as Spec)))
+        }
+        for (minusConfiguration in minusConfigurations) {
+            result.removeAll(getAllDeps(minusConfiguration.resolvedConfiguration.getFirstLevelModuleDependencies({ it instanceof ExternalDependency } as Spec)))
+        }
+        result
+    }
+
+    private Set<File> resolveFiles(Collection<Configuration> plusConfigurations, Collection<Configuration> minusConfigurations) {
+        def result = new LinkedHashSet()
+        for (plusConfiguration in plusConfigurations) {
+            result.addAll(plusConfiguration.files { it instanceof ExternalDependency })
+        }
+        for (minusConfiguration in minusConfigurations) {
+            result.removeAll(minusConfiguration.files { it instanceof ExternalDependency })
+        }
+        result
+    }
+
+    private getFiles(Set dependencies, String classifier) {
+        return project.configurations.detachedConfiguration((dependencies as Dependency[])).files.inject([:]) { result, sourceFile ->
+            String key = sourceFile.name.replace("-${classifier}.jar", '.jar')
+            result[key] = sourceFile
+            result
+        }
+    }
+
+    private List getResolvableDependenciesForAllResolvedDependencies(Set allResolvedDependencies, Closure configureClosure) {
+        return allResolvedDependencies.collect { ResolvedDependency resolvedDependency ->
+            def dependency = new DefaultExternalModuleDependency(resolvedDependency.moduleGroup, resolvedDependency.moduleName, resolvedDependency.moduleVersion,
+                    resolvedDependency.configuration)
+            dependency.transitive = false
+            configureClosure.call(dependency)
+            dependency
+        }
+    }
+
+    protected Set getAllDeps(Collection deps, Set allDeps = []) {
+        deps.each { ResolvedDependency resolvedDependency ->
+            def notSeenBefore = allDeps.add(resolvedDependency)
+            if (notSeenBefore) { // defend against circular dependencies
+                getAllDeps(resolvedDependency.children, allDeps)
+            }
+        }
+        allDeps
+    }
+
+    protected addSourceArtifact(DefaultExternalModuleDependency dependency) {
+        dependency.artifact { artifact ->
+            artifact.name = dependency.name
+            artifact.type = 'source'
+            artifact.extension = 'jar'
+            artifact.classifier = 'sources'
+        }
+    }
+
+    protected addJavadocArtifact(DefaultExternalModuleDependency dependency) {
+        dependency.artifact { artifact ->
+            artifact.name = dependency.name
+            artifact.type = 'javadoc'
+            artifact.extension = 'jar'
+            artifact.classifier = 'javadoc'
+        }
+    }
+
+    protected Path getPath(File file) {
+        return pathFactory.path(file)
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/internal/ModuleDependencyBuilder.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/internal/ModuleDependencyBuilder.groovy
new file mode 100644
index 0000000..e18e4b0
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/internal/ModuleDependencyBuilder.groovy
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.idea.model.internal
+
+import org.gradle.plugins.ide.idea.model.ModuleDependency
+
+/**
+ * @author Szczepan Faber, @date: 19.03.11
+ */
+class ModuleDependencyBuilder {
+    ModuleDependency create(gradleProject, String scope) {
+        if (gradleProject.hasProperty('ideaModule') && gradleProject.ideaModule) {
+            new ModuleDependency(gradleProject.ideaModule.moduleName, scope)
+        } else {
+            new ModuleDependency(gradleProject.name, scope)
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/package-info.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/package-info.java
new file mode 100644
index 0000000..d948a94
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/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.ide.idea.IdeaPlugin}.
+ */
+package org.gradle.plugins.ide.idea.model;
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/IdePlugin.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/IdePlugin.groovy
new file mode 100644
index 0000000..77cf591
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/IdePlugin.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.plugins.ide.internal;
+
+
+import org.apache.commons.lang.StringUtils
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.Task
+import org.gradle.api.tasks.Delete
+
+public abstract class IdePlugin implements Plugin<Project> {
+    private Task lifecycleTask;
+    private Task cleanTask;
+    protected Project project;
+
+    public void apply(Project target) {
+        project = target;
+        String lifecyleTaskName = getLifecycleTaskName();
+        lifecycleTask = target.task(lifecyleTaskName);
+        lifecycleTask.setGroup("IDE");
+        cleanTask = target.task(cleanName(lifecyleTaskName));
+        cleanTask.setGroup("IDE");
+        onApply(target);
+    }
+
+    public Task getLifecycleTask() {
+        return lifecycleTask;
+    }
+
+    public Task getCleanTask() {
+        return cleanTask;
+    }
+
+    public Task getCleanTask(Task worker) {
+        return project.getTasks().getByName(cleanName(worker.getName()));
+    }
+
+    private String cleanName(String taskName) {
+        return String.format("clean%s", StringUtils.capitalize(taskName));
+    }
+
+    protected void addWorker(Task worker) {
+        lifecycleTask.dependsOn(worker);
+        Delete cleanWorker = project.getTasks().add(cleanName(worker.getName()), Delete.class);
+        cleanWorker.delete(worker.getOutputs().getFiles());
+        cleanTask.dependsOn(cleanWorker);
+    }
+    
+    protected void onApply(Project target) {
+    }
+
+    protected abstract String getLifecycleTaskName();
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/configurer/DeduplicationTarget.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/configurer/DeduplicationTarget.groovy
new file mode 100644
index 0000000..31fbe7a
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/configurer/DeduplicationTarget.groovy
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.internal.configurer
+
+import org.gradle.api.Project
+
+/**
+ * @author Szczepan Faber, @date: 14.03.11
+ */
+ class DeduplicationTarget {
+
+     def String moduleName
+     def Project project
+     def Closure updateModuleName
+
+     Collection<String> getCandidateNames() {
+        def out = []
+        def p = project.parent
+        def currentName = moduleName
+        out << currentName
+        while (p) {
+            currentName = p.name + "-" + currentName
+            out.add(currentName)
+            p = p.parent
+        }
+        return out
+     }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/configurer/ModuleNameDeduper.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/configurer/ModuleNameDeduper.groovy
new file mode 100644
index 0000000..eba77bf
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/configurer/ModuleNameDeduper.groovy
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.internal.configurer
+
+/**
+ * Able to deduplicate names. Useful for IDE plugins to make sure module names (IDEA) or project names (Eclipse) are unique.
+ * <p>
+ * @author Szczepan Faber, @date 11.03.11
+ */
+class ModuleNameDeduper {
+
+    void dedupe(Collection<DeduplicationTarget> targets) {
+        def allNames = []
+        targets.each { target ->
+            def name = target.candidateNames.find { !allNames.contains(it) }
+            if (name) {
+                allNames << name
+                target.updateModuleName(name)
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/configurer/ProjectDeduper.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/configurer/ProjectDeduper.groovy
new file mode 100644
index 0000000..fceb77f
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/configurer/ProjectDeduper.groovy
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.internal.configurer
+
+import org.gradle.api.Project
+
+/**
+ * @author Szczepan Faber, @date: 14.03.11
+ */
+class ProjectDeduper {
+
+    def moduleNameDeduper = new ModuleNameDeduper()
+
+    void dedupe(Collection<Project> projects, Closure createDeduplicationTarget) {
+        //Deduper acts on first-come first-served basis.
+        //Therefore it's better if the inputs are sorted that first items are least wanted to be prefixed
+        //Hence I'm sorting by nesting level:
+        def sorted = projects.sort { (it.parent == null)? 0 : it.path.count(":") }
+        def deduplicationTargets = sorted.collect({ createDeduplicationTarget(it) })
+        moduleNameDeduper.dedupe(deduplicationTargets)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/generator/AbstractPersistableConfigurationObject.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/generator/AbstractPersistableConfigurationObject.groovy
new file mode 100644
index 0000000..a42a3bd
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/generator/AbstractPersistableConfigurationObject.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.plugins.ide.internal.generator;
+
+
+import org.gradle.plugins.ide.internal.generator.generator.PersistableConfigurationObject
+import org.gradle.util.UncheckedException
+
+public abstract class AbstractPersistableConfigurationObject implements PersistableConfigurationObject {
+    public void load(File inputFile) {
+        try {
+            InputStream inputStream = new FileInputStream(inputFile);
+            try {
+                load(inputStream);
+            } finally {
+                inputStream.close();
+            }
+        } catch (Exception e) {
+            throw UncheckedException.asUncheckedException(e);
+        }
+    }
+
+    public void loadDefaults() {
+        try {
+            InputStream inputStream = getClass().getResourceAsStream(getDefaultResourceName());
+            try {
+                load(inputStream);
+            } finally {
+                inputStream.close();
+            }
+        } catch (Exception e) {
+            throw UncheckedException.asUncheckedException(e);
+        }
+    }
+
+    public abstract void load(InputStream inputStream) throws Exception;
+
+    public void store(File outputFile) {
+        try {
+            OutputStream outputStream = new FileOutputStream(outputFile);
+            try {
+                store(outputStream);
+            } finally {
+                outputStream.close();
+            }
+        } catch (IOException e) {
+            throw UncheckedException.asUncheckedException(e);
+        }
+    }
+
+    public abstract void store(OutputStream outputStream);
+
+    protected abstract String getDefaultResourceName();
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/generator/PropertiesPersistableConfigurationObject.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/generator/PropertiesPersistableConfigurationObject.groovy
new file mode 100644
index 0000000..54bdfe1
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/generator/PropertiesPersistableConfigurationObject.groovy
@@ -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.plugins.ide.internal.generator;
+
+
+import org.gradle.util.UncheckedException;
+
+
+public abstract class PropertiesPersistableConfigurationObject extends AbstractPersistableConfigurationObject {
+    private Properties properties;
+
+    @Override
+    public void load(InputStream inputStream) throws Exception {
+        properties = new Properties();
+        properties.load(inputStream);
+        load(properties);
+    }
+
+    @Override
+    public void store(OutputStream outputStream) {
+        store(properties);
+        try {
+            properties.store(outputStream, "");
+        } catch (IOException e) {
+            throw UncheckedException.asUncheckedException(e);
+        }
+    }
+
+    protected abstract void store(Properties properties);
+
+    protected abstract void load(Properties properties);
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/generator/XmlPersistableConfigurationObject.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/generator/XmlPersistableConfigurationObject.groovy
new file mode 100644
index 0000000..38a9b61
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/generator/XmlPersistableConfigurationObject.groovy
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.internal.generator;
+
+
+import org.gradle.api.internal.XmlTransformer
+
+/**
+ * A {@link org.gradle.plugins.ide.internal.generator.generator.PersistableConfigurationObject} which is stored in an XML file.
+ */
+public abstract class XmlPersistableConfigurationObject extends AbstractPersistableConfigurationObject {
+    private final XmlTransformer xmlTransformer;
+    private Node xml;
+
+    protected XmlPersistableConfigurationObject(XmlTransformer xmlTransformer) {
+        this.xmlTransformer = xmlTransformer;
+    }
+
+    public Node getXml() {
+        return xml;
+    }
+
+    @Override
+    public void load(InputStream inputStream) throws Exception {
+        xml = new XmlParser().parse(inputStream);
+        load(xml);
+    }
+
+    @Override
+    public void store(OutputStream outputStream) {
+        store(xml);
+        xmlTransformer.transform(xml, outputStream);
+    }
+
+    /**
+     * Called immediately after the XML file has been read.
+     */
+    protected abstract void load(Node xml);
+
+    /**
+     * Called immediately before the XML file is to be written.
+     */
+    protected abstract void store(Node xml);
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/generator/generator/Generator.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/generator/generator/Generator.java
new file mode 100644
index 0000000..86e38c7
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/generator/generator/Generator.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.plugins.ide.internal.generator.generator;
+
+import java.io.File;
+
+/**
+ * Responsible for reading, configuring and writing a config object of type T to/from a file.
+ * @param <T>
+ */
+public interface Generator<T> {
+    T read(File inputFile);
+
+    T defaultInstance();
+
+    void configure(T object);
+
+    void write(T object, File outputFile);
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/generator/generator/PersistableConfigurationObject.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/generator/generator/PersistableConfigurationObject.java
new file mode 100644
index 0000000..3f40240
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/generator/generator/PersistableConfigurationObject.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.plugins.ide.internal.generator.generator;
+
+import java.io.File;
+
+public interface PersistableConfigurationObject {
+    void load(File inputFile);
+
+    void loadDefaults();
+
+    void store(File outputFile);
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/generator/generator/PersistableConfigurationObjectGenerator.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/generator/generator/PersistableConfigurationObjectGenerator.java
new file mode 100644
index 0000000..eae0fd6
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/generator/generator/PersistableConfigurationObjectGenerator.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.internal.generator.generator;
+
+import org.gradle.api.internal.Factory;
+
+import java.io.File;
+
+/**
+ * Adapts a {@link PersistableConfigurationObject} to a {@link
+ * Generator}.
+ *
+ * @param <T> the configuration object type.
+ */
+public abstract class PersistableConfigurationObjectGenerator<T extends PersistableConfigurationObject> implements Generator<T>, Factory<T> {
+    public T read(File inputFile) {
+        T obj = create();
+        obj.load(inputFile);
+        return obj;
+    }
+
+    public T defaultInstance() {
+        T obj = create();
+        obj.loadDefaults();
+        return obj;
+    }
+
+    public void write(T object, File outputFile) {
+        object.store(outputFile);
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/tooling/internal/provider/BuildModelAction.java b/subprojects/ide/src/main/groovy/org/gradle/tooling/internal/provider/BuildModelAction.java
new file mode 100644
index 0000000..708a759
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/tooling/internal/provider/BuildModelAction.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.provider;
+
+import org.gradle.BuildResult;
+import org.gradle.GradleLauncher;
+import org.gradle.initialization.GradleLauncherAction;
+import org.gradle.tooling.internal.protocol.BuildableProjectVersion1;
+import org.gradle.tooling.internal.protocol.ProjectVersion3;
+import org.gradle.tooling.internal.protocol.eclipse.EclipseProjectVersion3;
+
+public class BuildModelAction implements GradleLauncherAction<ProjectVersion3> {
+    private final ModelBuilder builder;
+
+    public BuildModelAction(Class<? extends ProjectVersion3> type) {
+        if (!type.isAssignableFrom(EclipseProjectVersion3.class)) {
+            throw new UnsupportedOperationException(String.format("Do not know how to build a model of type '%s'.", type.getSimpleName()));
+        }
+
+        boolean projectDependenciesOnly = !EclipseProjectVersion3.class.isAssignableFrom(type);
+        boolean includeTasks = BuildableProjectVersion1.class.isAssignableFrom(type);
+        builder = new ModelBuilder(includeTasks, projectDependenciesOnly);
+    }
+
+    public BuildResult run(GradleLauncher launcher) {
+        ModelBuildingAdapter adapter = new ModelBuildingAdapter(
+                new EclipsePluginApplier(), builder);
+        launcher.addListener(adapter);
+        return launcher.getBuildAnalysis();
+    }
+
+    public ProjectVersion3 getResult() {
+        return builder.getProject();
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/tooling/internal/provider/EclipsePluginApplier.java b/subprojects/ide/src/main/groovy/org/gradle/tooling/internal/provider/EclipsePluginApplier.java
new file mode 100644
index 0000000..1f6eca5
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/tooling/internal/provider/EclipsePluginApplier.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider;
+
+import org.gradle.api.Project;
+import org.gradle.api.internal.GradleInternal;
+import org.gradle.plugins.ide.eclipse.EclipsePlugin;
+import org.gradle.plugins.ide.eclipse.internal.EclipseNameDeduper;
+
+import java.util.Set;
+
+/**
+ * @author Szczepan Faber, @date: 25.03.11
+ */
+public class EclipsePluginApplier {
+    public void apply(GradleInternal gradle) {
+        Set<Project> allprojects = gradle.getRootProject().getAllprojects();
+        for (Project p : allprojects) {
+            p.getPlugins().apply(EclipsePlugin.class);
+            //TODO SF: this is temporary, until we figure out how to tackle this
+            new EclipseNameDeduper().configure(p);
+        }
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/tooling/internal/provider/ModelBuilder.java b/subprojects/ide/src/main/groovy/org/gradle/tooling/internal/provider/ModelBuilder.java
new file mode 100644
index 0000000..7a746a2
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/tooling/internal/provider/ModelBuilder.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider;
+
+import org.gradle.api.Project;
+import org.gradle.api.internal.GradleInternal;
+import org.gradle.plugins.ide.eclipse.EclipsePlugin;
+import org.gradle.plugins.ide.eclipse.model.ClasspathEntry;
+import org.gradle.plugins.ide.eclipse.model.EclipseClasspath;
+import org.gradle.plugins.ide.eclipse.model.EclipseModel;
+import org.gradle.tooling.internal.DefaultEclipseProject;
+import org.gradle.tooling.internal.protocol.ExternalDependencyVersion1;
+import org.gradle.tooling.internal.protocol.eclipse.EclipseProjectDependencyVersion2;
+import org.gradle.tooling.internal.protocol.eclipse.EclipseSourceDirectoryVersion1;
+import org.gradle.tooling.internal.provider.dependencies.EclipseProjectDependenciesFactory;
+import org.gradle.tooling.internal.provider.dependencies.ExternalDependenciesFactory;
+import org.gradle.tooling.internal.provider.dependencies.SourceDirectoriesFactory;
+import org.gradle.util.GUtil;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+* @author Adam Murdoch, Szczepan Faber, @date: 17.03.11
+*/
+public class ModelBuilder {
+    private final boolean includeTasks;
+    private boolean projectDependenciesOnly;
+    private DefaultEclipseProject currentProject;
+    private final Map<String, DefaultEclipseProject> projectMapping = new HashMap<String, DefaultEclipseProject>();
+    private GradleInternal gradle;
+
+    public ModelBuilder(boolean includeTasks, boolean projectDependenciesOnly) {
+        this.includeTasks = includeTasks;
+        this.projectDependenciesOnly = projectDependenciesOnly;
+    }
+
+    public void buildAll(GradleInternal gradle) {
+        this.gradle = gradle;
+        buildHierarchy(gradle.getRootProject());
+        populate(gradle.getRootProject());
+    }
+
+    public DefaultEclipseProject getProject() {
+        return currentProject;
+    }
+
+    private void addProject(Project project, DefaultEclipseProject eclipseProject) {
+        if (project == gradle.getDefaultProject()) {
+            currentProject = eclipseProject;
+        }
+        projectMapping.put(project.getPath(), eclipseProject);
+    }
+
+    private void populate(Project project) {
+        EclipseModel eclipseModel = project.getPlugins().getPlugin(EclipsePlugin.class).getModel();
+        EclipseClasspath classpath = eclipseModel.getClasspath();
+
+        classpath.setProjectDependenciesOnly(projectDependenciesOnly);
+        List<ClasspathEntry> entries = classpath.resolveDependencies();
+
+        List<ExternalDependencyVersion1> dependencies = new ExternalDependenciesFactory().create(project, entries);
+        List<EclipseProjectDependencyVersion2> projectDependencies = new EclipseProjectDependenciesFactory().create(projectMapping, entries);
+        List<EclipseSourceDirectoryVersion1> sourceDirectories = new SourceDirectoriesFactory().create(project, entries);
+
+        DefaultEclipseProject eclipseProject = projectMapping.get(project.getPath());
+        eclipseProject.setClasspath(dependencies);
+        eclipseProject.setProjectDependencies(projectDependencies);
+        eclipseProject.setSourceDirectories(sourceDirectories);
+        if (includeTasks) {
+            eclipseProject.setTasks(new TasksFactory().create(project, eclipseProject));
+        }
+
+        for (Project childProject : project.getChildProjects().values()) {
+            populate(childProject);
+        }
+    }
+
+    private DefaultEclipseProject buildHierarchy(Project project) {
+        List<DefaultEclipseProject> children = new ArrayList<DefaultEclipseProject>();
+        for (Project child : project.getChildProjects().values()) {
+            children.add(buildHierarchy(child));
+        }
+
+        EclipseModel eclipseModel = project.getPlugins().getPlugin(EclipsePlugin.class).getModel();
+        org.gradle.plugins.ide.eclipse.model.EclipseProject internalProject = eclipseModel.getProject();
+        String name = internalProject.getName();
+        String description = GUtil.elvis(internalProject.getComment(), null);
+        DefaultEclipseProject eclipseProject = new DefaultEclipseProject(name, project.getPath(), description, project.getProjectDir(), children);
+        for (DefaultEclipseProject child : children) {
+            child.setParent(eclipseProject);
+        }
+        addProject(project, eclipseProject);
+        return eclipseProject;
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/main/groovy/org/gradle/tooling/internal/provider/ModelBuildingAdapter.java b/subprojects/ide/src/main/groovy/org/gradle/tooling/internal/provider/ModelBuildingAdapter.java
new file mode 100644
index 0000000..43f6141
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/tooling/internal/provider/ModelBuildingAdapter.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider;
+
+import org.gradle.BuildAdapter;
+import org.gradle.api.internal.GradleInternal;
+import org.gradle.api.invocation.Gradle;
+
+/**
+ * @author Szczepan Faber, @date: 25.03.11
+ */
+public class ModelBuildingAdapter extends BuildAdapter {
+
+    EclipsePluginApplier applier;
+    ModelBuilder builder;
+
+    public ModelBuildingAdapter(EclipsePluginApplier applier, ModelBuilder builder) {
+        this.applier = applier;
+        this.builder = builder;
+    }
+
+    @Override
+    public void projectsEvaluated(Gradle gradle) {
+        applier.apply((GradleInternal) gradle);
+        builder.buildAll((GradleInternal) gradle);
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/tooling/internal/provider/TasksFactory.java b/subprojects/ide/src/main/groovy/org/gradle/tooling/internal/provider/TasksFactory.java
new file mode 100644
index 0000000..5f33eee
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/tooling/internal/provider/TasksFactory.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.provider;
+
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.tooling.internal.DefaultTask;
+import org.gradle.tooling.internal.protocol.eclipse.EclipseProjectVersion3;
+import org.gradle.tooling.internal.protocol.eclipse.EclipseTaskVersion1;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TasksFactory {
+    public List<EclipseTaskVersion1> create(Project project, EclipseProjectVersion3 eclipseProject) {
+        List<EclipseTaskVersion1> tasks = new ArrayList<EclipseTaskVersion1>();
+        for (final Task task : project.getTasks()) {
+            tasks.add(new DefaultTask(eclipseProject, task.getPath(), task.getName(), task.getDescription()));
+        }
+        return tasks;
+    }
+
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/tooling/internal/provider/dependencies/EclipseProjectDependenciesFactory.java b/subprojects/ide/src/main/groovy/org/gradle/tooling/internal/provider/dependencies/EclipseProjectDependenciesFactory.java
new file mode 100644
index 0000000..17b3775
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/tooling/internal/provider/dependencies/EclipseProjectDependenciesFactory.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider.dependencies;
+
+import org.apache.commons.lang.StringUtils;
+import org.gradle.plugins.ide.eclipse.model.ClasspathEntry;
+import org.gradle.plugins.ide.eclipse.model.ProjectDependency;
+import org.gradle.tooling.internal.DefaultEclipseProjectDependency;
+import org.gradle.tooling.internal.protocol.eclipse.EclipseProjectDependencyVersion2;
+import org.gradle.tooling.internal.protocol.eclipse.HierarchicalEclipseProjectVersion1;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Szczepan Faber, @date: 24.03.11
+ */
+public class EclipseProjectDependenciesFactory {
+    public List<EclipseProjectDependencyVersion2> create(final Map<String, ? extends HierarchicalEclipseProjectVersion1> projectMapping, List<ClasspathEntry> entries) {
+        final LinkedList<EclipseProjectDependencyVersion2> dependencies = new LinkedList<EclipseProjectDependencyVersion2>();
+
+        for (ClasspathEntry entry : entries) {
+            if (entry instanceof ProjectDependency) {
+                final ProjectDependency projectDependency = (ProjectDependency) entry;
+                final String path = StringUtils.removeStart(projectDependency.getPath(), "/");
+                dependencies.add(new DefaultEclipseProjectDependency(path, projectMapping.get(projectDependency.getGradlePath())));
+            }
+        }
+        return dependencies;
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/tooling/internal/provider/dependencies/ExternalDependenciesFactory.java b/subprojects/ide/src/main/groovy/org/gradle/tooling/internal/provider/dependencies/ExternalDependenciesFactory.java
new file mode 100644
index 0000000..b8d6183
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/tooling/internal/provider/dependencies/ExternalDependenciesFactory.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider.dependencies;
+
+import org.gradle.api.Project;
+import org.gradle.plugins.ide.eclipse.model.ClasspathEntry;
+import org.gradle.plugins.ide.eclipse.model.Library;
+import org.gradle.tooling.internal.DefaultExternalDependency;
+import org.gradle.tooling.internal.protocol.ExternalDependencyVersion1;
+
+import java.io.File;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author Szczepan Faber, @date: 23.03.11
+ */
+public class ExternalDependenciesFactory {
+    public List<ExternalDependencyVersion1> create(final Project project, List<ClasspathEntry> entries) {
+        List<ExternalDependencyVersion1> dependencies = new LinkedList<ExternalDependencyVersion1>();
+        for (ClasspathEntry entry : entries) {
+            if (entry instanceof Library) {
+                Library library = (Library) entry;
+                final File file = project.file(library.getPath());
+                final File source = library.getSourcePath() == null ? null : project.file(library.getSourcePath());
+                final File javadoc = library.getJavadocPath() == null ? null : project.file(library.getJavadocPath());
+                dependencies.add(new DefaultExternalDependency(file, javadoc, source));
+            }
+        }
+        return dependencies;
+    }
+
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/tooling/internal/provider/dependencies/SourceDirectoriesFactory.java b/subprojects/ide/src/main/groovy/org/gradle/tooling/internal/provider/dependencies/SourceDirectoriesFactory.java
new file mode 100644
index 0000000..1ee3ed9
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/tooling/internal/provider/dependencies/SourceDirectoriesFactory.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider.dependencies;
+
+import org.gradle.api.Project;
+import org.gradle.plugins.ide.eclipse.model.ClasspathEntry;
+import org.gradle.plugins.ide.eclipse.model.SourceFolder;
+import org.gradle.tooling.internal.DefaultEclipseSourceDirectory;
+import org.gradle.tooling.internal.protocol.eclipse.EclipseSourceDirectoryVersion1;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Szczepan Faber, @date: 20.03.11
+ */
+public class SourceDirectoriesFactory {
+    public List<EclipseSourceDirectoryVersion1> create(Project project, List<ClasspathEntry> entries) {
+        List<EclipseSourceDirectoryVersion1> sourceDirectories = new ArrayList<EclipseSourceDirectoryVersion1>();
+        for (ClasspathEntry entry : entries) {
+            if (entry instanceof SourceFolder) {
+                String path = ((SourceFolder) entry).getPath();
+                sourceDirectories.add(sourceDirectory(project, path));
+            }
+        }
+        return sourceDirectories;
+    }
+
+    private EclipseSourceDirectoryVersion1 sourceDirectory(Project project, String path) {
+        return new DefaultEclipseSourceDirectory(path, project.file(path));
+    }
+}
diff --git a/subprojects/ide/src/main/resources/META-INF/gradle-plugins/eclipse.properties b/subprojects/ide/src/main/resources/META-INF/gradle-plugins/eclipse.properties
new file mode 100644
index 0000000..be1544d
--- /dev/null
+++ b/subprojects/ide/src/main/resources/META-INF/gradle-plugins/eclipse.properties
@@ -0,0 +1 @@
+implementation-class=org.gradle.plugins.ide.eclipse.EclipsePlugin
diff --git a/subprojects/ide/src/main/resources/META-INF/gradle-plugins/idea.properties b/subprojects/ide/src/main/resources/META-INF/gradle-plugins/idea.properties
new file mode 100644
index 0000000..26c6976
--- /dev/null
+++ b/subprojects/ide/src/main/resources/META-INF/gradle-plugins/idea.properties
@@ -0,0 +1 @@
+implementation-class=org.gradle.plugins.ide.idea.IdeaPlugin
diff --git a/subprojects/eclipse/src/main/resources/org/gradle/plugins/eclipse/model/defaultClasspath.xml b/subprojects/ide/src/main/resources/org/gradle/plugins/ide/eclipse/model/defaultClasspath.xml
similarity index 100%
rename from subprojects/eclipse/src/main/resources/org/gradle/plugins/eclipse/model/defaultClasspath.xml
rename to subprojects/ide/src/main/resources/org/gradle/plugins/ide/eclipse/model/defaultClasspath.xml
diff --git a/subprojects/eclipse/src/main/resources/org/gradle/plugins/eclipse/model/defaultJdtPrefs.properties b/subprojects/ide/src/main/resources/org/gradle/plugins/ide/eclipse/model/defaultJdtPrefs.properties
similarity index 100%
rename from subprojects/eclipse/src/main/resources/org/gradle/plugins/eclipse/model/defaultJdtPrefs.properties
rename to subprojects/ide/src/main/resources/org/gradle/plugins/ide/eclipse/model/defaultJdtPrefs.properties
diff --git a/subprojects/eclipse/src/main/resources/org/gradle/plugins/eclipse/model/defaultProject.xml b/subprojects/ide/src/main/resources/org/gradle/plugins/ide/eclipse/model/defaultProject.xml
similarity index 100%
rename from subprojects/eclipse/src/main/resources/org/gradle/plugins/eclipse/model/defaultProject.xml
rename to subprojects/ide/src/main/resources/org/gradle/plugins/ide/eclipse/model/defaultProject.xml
diff --git a/subprojects/ide/src/main/resources/org/gradle/plugins/ide/eclipse/model/defaultWtpComponent.xml b/subprojects/ide/src/main/resources/org/gradle/plugins/ide/eclipse/model/defaultWtpComponent.xml
new file mode 100644
index 0000000..b1eecfe
--- /dev/null
+++ b/subprojects/ide/src/main/resources/org/gradle/plugins/ide/eclipse/model/defaultWtpComponent.xml
@@ -0,0 +1,3 @@
+<project-modules id="moduleCoreId" project-version="2.0">
+    <wb-module/>
+</project-modules>
\ No newline at end of file
diff --git a/subprojects/ide/src/main/resources/org/gradle/plugins/ide/eclipse/model/defaultWtpFacet.xml b/subprojects/ide/src/main/resources/org/gradle/plugins/ide/eclipse/model/defaultWtpFacet.xml
new file mode 100644
index 0000000..ebf2004
--- /dev/null
+++ b/subprojects/ide/src/main/resources/org/gradle/plugins/ide/eclipse/model/defaultWtpFacet.xml
@@ -0,0 +1,4 @@
+<faceted-project>
+    <fixed facet="jst.java"/>
+    <fixed facet="jst.web"/>
+</faceted-project>
\ No newline at end of file
diff --git a/subprojects/ide/src/main/resources/org/gradle/plugins/ide/idea/model/defaultModule.xml b/subprojects/ide/src/main/resources/org/gradle/plugins/ide/idea/model/defaultModule.xml
new file mode 100644
index 0000000..eb4512d
--- /dev/null
+++ b/subprojects/ide/src/main/resources/org/gradle/plugins/ide/idea/model/defaultModule.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+    <component name="NewModuleRootManager" inherit-compiler-output="true">
+        <exclude-output/>
+        <orderEntry type="inheritedJdk"/>
+        <content url="file://$MODULE_DIR$">
+        </content>
+        <orderEntry type="sourceFolder" forTests="false"/>
+    </component>
+    <component name="ModuleRootManager"/>
+</module>
+
diff --git a/subprojects/idea/src/main/resources/org/gradle/plugins/idea/model/defaultProject.xml b/subprojects/ide/src/main/resources/org/gradle/plugins/ide/idea/model/defaultProject.xml
similarity index 100%
rename from subprojects/idea/src/main/resources/org/gradle/plugins/idea/model/defaultProject.xml
rename to subprojects/ide/src/main/resources/org/gradle/plugins/ide/idea/model/defaultProject.xml
diff --git a/subprojects/idea/src/main/resources/org/gradle/plugins/idea/model/defaultWorkspace.xml b/subprojects/ide/src/main/resources/org/gradle/plugins/ide/idea/model/defaultWorkspace.xml
similarity index 100%
rename from subprojects/idea/src/main/resources/org/gradle/plugins/idea/model/defaultWorkspace.xml
rename to subprojects/ide/src/main/resources/org/gradle/plugins/ide/idea/model/defaultWorkspace.xml
diff --git a/subprojects/ide/src/main/resources/org/gradle/plugins/ide/idea/package-info.java b/subprojects/ide/src/main/resources/org/gradle/plugins/ide/idea/package-info.java
new file mode 100644
index 0000000..a6e131b
--- /dev/null
+++ b/subprojects/ide/src/main/resources/org/gradle/plugins/ide/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.ide.idea;
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/EclipsePluginTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/EclipsePluginTest.groovy
new file mode 100644
index 0000000..3e41d30
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/EclipsePluginTest.groovy
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.eclipse
+
+import org.gradle.api.Project
+import org.gradle.api.Task
+import org.gradle.api.internal.project.DefaultProject
+import org.gradle.api.tasks.Delete
+import org.gradle.plugins.ide.eclipse.model.BuildCommand
+import org.gradle.plugins.ide.eclipse.model.Facet
+import org.gradle.plugins.ide.eclipse.model.WbResource
+import org.gradle.util.HelperUtil
+import spock.lang.Specification
+
+/**
+ * @author Hans Dockter
+ */
+class EclipsePluginTest extends Specification {
+    private final DefaultProject project = HelperUtil.createRootProject()
+    private final EclipsePlugin eclipsePlugin = new EclipsePlugin()
+
+    def applyToBaseProject_shouldOnlyHaveEclipseProjectTask() {
+        when:
+        eclipsePlugin.apply(project)
+
+        then:
+        project.tasks.findByPath(':eclipseClasspath') == null
+        assertThatCleanEclipseDependsOn(project, project.cleanEclipseProject)
+        checkEclipseProjectTask([], [])
+    }
+
+    def applyToJavaProject_shouldOnlyHaveProjectAndClasspathTaskForJava() {
+        when:
+        project.apply(plugin: 'java-base')
+        eclipsePlugin.apply(project)
+
+        then:
+        assertThatCleanEclipseDependsOn(project, project.cleanEclipseProject)
+        assertThatCleanEclipseDependsOn(project, project.cleanEclipseClasspath)
+        checkEclipseProjectTask([new BuildCommand('org.eclipse.jdt.core.javabuilder')], ['org.eclipse.jdt.core.javanature'])
+        checkEclipseClasspath([])
+        checkEclipseJdt()
+
+        when:
+        project.apply(plugin: 'java')
+
+        then:
+        checkEclipseClasspath([project.configurations.testRuntime])
+    }
+
+    def applyToWarProject_shouldHaveWebProjectAndClasspathTask() {
+        when:
+        project.apply(plugin: 'war')
+        eclipsePlugin.apply(project)
+
+        then:
+        assertThatCleanEclipseDependsOn(project, project.cleanEclipseProject)
+        assertThatCleanEclipseDependsOn(project, project.cleanEclipseClasspath)
+        assertThatCleanEclipseDependsOn(project, project.cleanEclipseWtpComponent)
+        assertThatCleanEclipseDependsOn(project, project.cleanEclipseWtpFacet)
+        checkEclipseProjectTask([
+                new BuildCommand('org.eclipse.jdt.core.javabuilder'),
+                new BuildCommand('org.eclipse.wst.common.project.facet.core.builder'),
+                new BuildCommand('org.eclipse.wst.validation.validationbuilder')],
+                ['org.eclipse.jdt.core.javanature',
+                        'org.eclipse.wst.common.project.facet.core.nature',
+                        'org.eclipse.wst.common.modulecore.ModuleCoreNature',
+                        'org.eclipse.jem.workbench.JavaEMFNature'])
+        checkEclipseClasspath([project.configurations.testRuntime])
+        checkEclipseWtpComponent()
+        checkEclipseWtpFacet()
+    }
+
+    def applyToScalaProject_shouldHaveProjectAndClasspathTaskForScala() {
+        when:
+        project.apply(plugin: 'scala-base')
+        eclipsePlugin.apply(project)
+
+        then:
+        assertThatCleanEclipseDependsOn(project, project.cleanEclipseProject)
+        assertThatCleanEclipseDependsOn(project, project.cleanEclipseClasspath)
+        checkEclipseProjectTask([new BuildCommand('ch.epfl.lamp.sdt.core.scalabuilder')],
+                ['ch.epfl.lamp.sdt.core.scalanature', 'org.eclipse.jdt.core.javanature'])
+        checkEclipseClasspath([])
+
+        when:
+        project.apply(plugin: 'scala')
+
+        then:
+        checkEclipseClasspath([project.configurations.testRuntime])
+    }
+
+    def applyToGroovyProject_shouldHaveProjectAndClasspathTaskForGroovy() {
+        when:
+        project.apply(plugin: 'groovy-base')
+        eclipsePlugin.apply(project)
+
+        then:
+        assertThatCleanEclipseDependsOn(project, project.cleanEclipseProject)
+        assertThatCleanEclipseDependsOn(project, project.cleanEclipseClasspath)
+        checkEclipseProjectTask([new BuildCommand('org.eclipse.jdt.core.javabuilder')], ['org.eclipse.jdt.groovy.core.groovyNature',
+                'org.eclipse.jdt.core.javanature'])
+        checkEclipseClasspath([])
+
+        when:
+        project.apply(plugin: 'groovy')
+
+        then:
+        checkEclipseClasspath([project.configurations.testRuntime])
+    }
+
+    def "creates empty classpath model for non java projects"() {
+        when:
+        eclipsePlugin.apply(project)
+
+        then:
+        eclipsePlugin.model.classpath
+        eclipsePlugin.model.classpath.classesOutputDir
+    }
+
+    private void checkEclipseProjectTask(List buildCommands, List natures) {
+        GenerateEclipseProject eclipseProjectTask = project.eclipseProject
+        assert eclipseProjectTask instanceof GenerateEclipseProject
+        assert project.eclipse.taskDependencies.getDependencies(project.eclipse).contains(eclipseProjectTask)
+        assert eclipseProjectTask.buildCommands == buildCommands
+        assert eclipseProjectTask.natures == natures
+        assert eclipseProjectTask.links == [] as Set
+        assert eclipseProjectTask.referencedProjects == [] as Set
+        assert eclipseProjectTask.comment == null
+        assert eclipseProjectTask.projectName == project.name
+        assert eclipseProjectTask.outputFile == project.file('.project')
+    }
+
+    private void checkEclipseClasspath(def configurations) {
+        GenerateEclipseClasspath eclipseClasspath = project.eclipseClasspath
+        assert eclipseClasspath instanceof GenerateEclipseClasspath
+        assert project.eclipse.taskDependencies.getDependencies(project.eclipse).contains(eclipseClasspath)
+        assert eclipseClasspath.sourceSets == project.sourceSets
+        assert eclipseClasspath.plusConfigurations == configurations
+        assert eclipseClasspath.minusConfigurations == []
+        assert eclipseClasspath.containers == ['org.eclipse.jdt.launching.JRE_CONTAINER'] as Set
+        assert eclipseClasspath.outputFile == project.file('.classpath')
+        assert eclipseClasspath.defaultOutputDir == new File(project.projectDir, 'bin')
+    }
+
+    private void checkEclipseJdt() {
+        GenerateEclipseJdt eclipseJdt = project.eclipseJdt
+        assert project.eclipse.taskDependencies.getDependencies(project.eclipse).contains(eclipseJdt)
+        assert eclipseJdt.sourceCompatibility == project.sourceCompatibility
+        assert eclipseJdt.targetCompatibility == project.targetCompatibility
+        assert eclipseJdt.outputFile == project.file('.settings/org.eclipse.jdt.core.prefs')
+    }
+
+    private void checkEclipseWtpFacet() {
+        GenerateEclipseWtpFacet eclipseWtpFacet = project.eclipseWtpFacet
+        assert eclipseWtpFacet instanceof GenerateEclipseWtpFacet
+        assert project.eclipse.taskDependencies.getDependencies(project.eclipse).contains(eclipseWtpFacet)
+        assert eclipseWtpFacet.inputFile == project.file('.settings/org.eclipse.wst.common.project.facet.core.xml')
+        assert eclipseWtpFacet.outputFile == project.file('.settings/org.eclipse.wst.common.project.facet.core.xml')
+        assert eclipseWtpFacet.facets == [new Facet("jst.web", "2.4"), new Facet("jst.java", "5.0")]
+    }
+
+    private void checkEclipseWtpComponent() {
+        def eclipseWtpComponent = project.eclipseWtpComponent
+        assert eclipseWtpComponent instanceof GenerateEclipseWtpComponent
+        assert project.eclipse.taskDependencies.getDependencies(project.eclipse).contains(eclipseWtpComponent)
+        assert eclipseWtpComponent.sourceDirs == project.sourceSets.main.allSource.srcDirs
+        assert eclipseWtpComponent.plusConfigurations == [project.configurations.runtime] as Set
+        assert eclipseWtpComponent.minusConfigurations == [project.configurations.providedRuntime] as Set
+        assert eclipseWtpComponent.deployName == project.name
+        assert eclipseWtpComponent.contextPath == project.war.baseName
+        assert eclipseWtpComponent.inputFile == project.file('.settings/org.eclipse.wst.common.component')
+        assert eclipseWtpComponent.outputFile == project.file('.settings/org.eclipse.wst.common.component')
+        assert eclipseWtpComponent.variables == [:]
+        assert eclipseWtpComponent.resources == [new WbResource('/', project.convention.plugins.war.webAppDirName)]
+    }
+
+    void assertThatCleanEclipseDependsOn(Project project, Task dependsOnTask) {
+        assert dependsOnTask instanceof Delete
+        assert project.cleanEclipse.taskDependencies.getDependencies(project.cleanEclipse).contains(dependsOnTask)
+    }
+}
+
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseClasspathTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseClasspathTest.groovy
new file mode 100644
index 0000000..b71a31e
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseClasspathTest.groovy
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.eclipse;
+
+
+import org.gradle.api.internal.ConventionTask
+import org.gradle.api.tasks.AbstractSpockTaskTest
+import org.gradle.plugins.ide.eclipse.model.EclipseClasspath
+
+/**
+ * @author Hans Dockter
+ */
+public class GenerateEclipseClasspathTest extends AbstractSpockTaskTest {
+
+    private GenerateEclipseClasspath eclipseClasspath;
+
+    ConventionTask getTask() {
+        return eclipseClasspath
+    }
+
+    def setup() {
+        eclipseClasspath = createTask(GenerateEclipseClasspath.class);
+        eclipseClasspath.classpath = new EclipseClasspath()
+    }
+
+    def containers_shouldAdd() {
+        when:
+        eclipseClasspath.containers "container1"
+        eclipseClasspath.containers "container2"
+
+        then:
+        eclipseClasspath.containers == ['container1', 'container2'] as Set
+    }
+
+    def variables_shouldAdd() {
+        when:
+        eclipseClasspath.variables variable1: 'value1'
+        eclipseClasspath.variables variable2: 'value2'
+
+        then:
+        eclipseClasspath.variables == [variable1: 'value1', variable2: 'value2']
+    }
+}
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseProjectTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseProjectTest.groovy
new file mode 100644
index 0000000..c446df0
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseProjectTest.groovy
@@ -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.plugins.ide.eclipse
+
+import org.gradle.api.internal.ConventionTask
+import org.gradle.api.tasks.AbstractSpockTaskTest
+import org.gradle.plugins.ide.eclipse.model.BuildCommand
+import org.gradle.plugins.ide.eclipse.model.EclipseProject
+
+/**
+ * @author Hans Dockter
+ */
+class GenerateEclipseProjectTest extends AbstractSpockTaskTest {
+    GenerateEclipseProject eclipseProject
+
+    ConventionTask getTask() {
+        return eclipseProject
+    }
+
+    def setup() {
+        eclipseProject = createTask(GenerateEclipseProject.class);
+        eclipseProject.projectModel = new EclipseProject()
+    }
+
+    def natures_shouldAdd() {
+        when:
+        eclipseProject.natures 'nature1'
+        eclipseProject.natures 'nature2'
+
+        then:
+        eclipseProject.natures == ['nature1', 'nature2']
+    }
+
+    def buildCommands_shouldAdd() {
+        when:
+        eclipseProject.buildCommand 'command1', key1: 'value1'
+        eclipseProject.buildCommand 'command2'
+
+        then:
+        eclipseProject.buildCommands as List == [new BuildCommand('command1', [key1: 'value1']), new BuildCommand('command2')]
+    }
+}
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpComponentTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpComponentTest.groovy
new file mode 100644
index 0000000..738e4cc
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpComponentTest.groovy
@@ -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.plugins.ide.eclipse
+
+import org.gradle.api.internal.ConventionTask
+import org.gradle.api.tasks.AbstractSpockTaskTest
+import org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent
+import org.gradle.plugins.ide.eclipse.model.WbProperty
+import org.gradle.plugins.ide.eclipse.model.WbResource
+
+/**
+ * @author Hans Dockter
+ */
+public class GenerateEclipseWtpComponentTest extends AbstractSpockTaskTest {
+    private eclipseComponent = createTask(GenerateEclipseWtpComponent)
+
+    def setup() {
+        eclipseComponent.component = new EclipseWtpComponent()
+    }
+
+    ConventionTask getTask() { eclipseComponent }
+
+    def "property should add"() {
+        when:
+        eclipseComponent.property name: 'prop1', value: 'value1'
+        eclipseComponent.property name: 'prop2', value: 'value2'
+
+        then:
+        eclipseComponent.properties == [new WbProperty('prop1', 'value1'), new WbProperty('prop2', 'value2')]
+    }
+
+    def "resource should add"() {
+        when:
+        eclipseComponent.resource deployPath: 'path1', sourcePath: 'sourcepath1'
+        eclipseComponent.resource deployPath: 'path2', sourcePath: 'sourcepath2'
+
+        then:
+        eclipseComponent.resources == [new WbResource('path1', 'sourcepath1'), new WbResource('path2', 'sourcepath2')]
+    }
+
+    def "variables should add"() {
+        when:
+        eclipseComponent.variables variable1: 'value1'
+        eclipseComponent.variables variable2: 'value2'
+
+        then:
+        eclipseComponent.variables == [variable1: 'value1', variable2: 'value2']
+    }
+}
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpFacetTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpFacetTest.groovy
new file mode 100644
index 0000000..0b4676c
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpFacetTest.groovy
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.eclipse
+
+import org.gradle.api.tasks.AbstractSpockTaskTest
+import org.gradle.plugins.ide.eclipse.model.EclipseWtpFacet
+import org.gradle.plugins.ide.eclipse.model.Facet
+
+/**
+ * @author Hans Dockter
+ */
+public class GenerateEclipseWtpFacetTest extends AbstractSpockTaskTest {
+    private eclipseFacet = createTask(GenerateEclipseWtpFacet)
+
+    def setup() {
+        eclipseFacet.facet = new EclipseWtpFacet()
+    }
+
+    GenerateEclipseWtpFacet getTask() {
+        return eclipseFacet
+    }
+
+    def "facet should add"() {
+        when:
+        task.facet name: 'facet1', version: '1.0'
+        task.facet name: 'facet2', version: '2.0'
+
+        then:
+        task.facets == [new Facet('facet1', '1.0'), new Facet('facet2', '2.0')]
+    }
+}
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/ClasspathTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/ClasspathTest.groovy
new file mode 100644
index 0000000..5e187d2
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/ClasspathTest.groovy
@@ -0,0 +1,101 @@
+/*
+ * 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.plugins.ide.eclipse.model;
+
+
+import org.gradle.api.internal.XmlTransformer
+import org.gradle.util.TemporaryFolder
+import org.junit.Rule
+import spock.lang.Specification
+
+/**
+ * @author Hans Dockter
+ */
+public class ClasspathTest extends Specification {
+    private static final CUSTOM_ENTRIES = [
+            new ProjectDependency("/test2", false, null, [] as Set, null),
+            new Container("org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6",
+                false, null, [] as Set),
+            new Library("/apache-ant-1.7.1/lib/ant-antlr.jar", false, null, [] as Set, null, null),
+            new SourceFolder("src", null, [] as Set, "bin2", [], []),
+            new Variable("GRADLE_CACHE/ant-1.6.5.jar", false, null, [] as Set, null, null),
+            new Container("org.eclipse.jdt.USER_LIBRARY/gradle", false, null, [] as Set),
+            new Output("bin")]
+    private static final PROJECT_DEPENDENCY = [CUSTOM_ENTRIES[0]]
+    private static final ALL_DEPENDENCIES = [CUSTOM_ENTRIES[0], CUSTOM_ENTRIES[2]]
+
+    private final Classpath classpath = new Classpath(new XmlTransformer())
+
+    @Rule
+    public TemporaryFolder tmpDir = new TemporaryFolder()
+
+    def "load from reader"() {
+        when:
+        classpath.load(customClasspathReader)
+
+        then:
+        classpath.entries == CUSTOM_ENTRIES
+    }
+
+    def "configure overwrites dependencies and appends all other entries"() {
+        def constructorEntries = [createSomeLibrary()]
+
+        when:
+        classpath.load(customClasspathReader)
+        def newEntries = constructorEntries + PROJECT_DEPENDENCY
+        classpath.configure(newEntries)
+
+        then:
+        def entriesToBeKept = CUSTOM_ENTRIES - ALL_DEPENDENCIES
+        classpath.entries ==  entriesToBeKept + newEntries
+    }
+
+    def "load defaults"() {
+        when:
+        classpath.loadDefaults()
+
+        then:
+        classpath.entries == []
+    }
+
+    def "toXml contains custom values"() {
+        def constructorEntries = [createSomeLibrary()]
+
+        when:
+        classpath.load(customClasspathReader)
+        classpath.configure(constructorEntries)
+        def xml = getToXmlReader()
+        def other = new Classpath(new XmlTransformer())
+        other.load(xml)
+
+        then:
+        classpath == other
+    }
+
+    private InputStream getCustomClasspathReader() {
+        return getClass().getResourceAsStream('customClasspath.xml')
+    }
+
+    private Library createSomeLibrary() {
+        return new Library("/somepath", true, null, [] as Set, null, null)
+    }
+
+    private InputStream getToXmlReader() {
+        ByteArrayOutputStream toXmlText = new ByteArrayOutputStream()
+        classpath.store(toXmlText)
+        return new ByteArrayInputStream(toXmlText.toByteArray())
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/ContainerTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/ContainerTest.groovy
new file mode 100644
index 0000000..78d79ea
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/ContainerTest.groovy
@@ -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.plugins.ide.eclipse.model
+
+import spock.lang.Specification
+
+/**
+ * @author Hans Dockter
+ */
+
+class ContainerTest extends Specification {
+    final static String XML_TEXT = '''
+                <classpathentry exported="true" kind="con" path="somePath">
+                    <attributes>
+                        <attribute name="org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY" value="mynative"/>
+                    </attributes>
+                    <accessrules>
+                        <accessrule kind="nonaccessible" pattern="secret**"/>
+                    </accessrules>
+                </classpathentry>'''
+
+    def canReadFromXml() {
+        when:
+        Container container = new Container(new XmlParser().parseText(XML_TEXT))
+
+        then:
+        container == createContainer()
+    }
+
+    def canWriteToXml() {
+        Node rootNode = new Node(null, 'root')
+
+        when:
+        createContainer().appendNode(rootNode)
+
+        then:
+        new Container(rootNode.classpathentry[0]) == createContainer()
+    }
+
+    def equality() {
+        Container container = createContainer()
+        container.nativeLibraryLocation += 'x'
+
+        expect:
+        container != createContainer()
+    }
+
+    private Container createContainer() {
+        return new Container('somePath', true, 'mynative', [new AccessRule('nonaccessible', 'secret**')] as Set)
+    }
+
+
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/EclipseModelTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/EclipseModelTest.groovy
new file mode 100644
index 0000000..b271623
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/EclipseModelTest.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.ide.eclipse.model
+
+import spock.lang.Specification
+
+/**
+ * Author: Szczepan Faber, created at: 4/19/11
+ */
+class EclipseModelTest extends Specification {
+
+    EclipseModel model = new EclipseModel(classpath: new EclipseClasspath(), wtp: new EclipseWtp(component: new EclipseWtpComponent()))
+
+    def "enables setting path variables even if wtp is not configured"() {
+        given:
+        model.wtp.component = null
+
+        when:
+        model.pathVariables(one: new File('.'))
+        model.pathVariables(two: new File('.'))
+
+        then:
+        model.classpath.pathVariables == [one: new File('.'), two: new File('.')]
+    }
+
+    def "enables setting path variables"() {
+        when:
+        model.pathVariables(one: new File('.'))
+        model.pathVariables(two: new File('.'))
+
+        then:
+        model.classpath.pathVariables == [one: new File('.'), two: new File('.')]
+        model.wtp.component.pathVariables == [one: new File('.'), two: new File('.')]
+    }
+}
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/EclipseProjectTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/EclipseProjectTest.groovy
new file mode 100644
index 0000000..c2ea5f8
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/EclipseProjectTest.groovy
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.eclipse.model
+
+import org.gradle.api.InvalidUserDataException
+import spock.lang.Specification
+
+/**
+ * Author: Szczepan Faber, created at: 4/17/11
+ */
+class EclipseProjectTest extends Specification {
+
+    def eclipseProject = new EclipseProject()
+
+    def "allows adding linked resources"() {
+        when:
+        eclipseProject.linkedResource(name: 'foo', type: 'folder', location: '/stuff/foo')
+        eclipseProject.linkedResource(name: 'bar', type: 'uri', locationUri: 'file:///stuff/bar')
+
+        then:
+        2 == eclipseProject.linkedResources.size()
+    }
+
+    def "complains when invalid link created"() {
+        when:
+        eclipseProject.linkedResource(name: 'foo', type: 'folder', wrongKey: '/stuff/foo')
+
+        then:
+        thrown(InvalidUserDataException.class)
+
+        when:
+        eclipseProject.linkedResource(name: 'foo', type: 'folder', location: '/stuff/foo', locationUri: 'file:///boooo')
+
+        then:
+        thrown(AssertionError.class)
+
+        when:
+        eclipseProject.linkedResource(name: 'foo', type: 'folder')
+
+        then:
+        thrown(AssertionError.class)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/FacetTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/FacetTest.groovy
new file mode 100644
index 0000000..02f115e
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/FacetTest.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.plugins.ide.eclipse.model
+
+import spock.lang.Specification
+
+/**
+ * @author Hans Dockter
+ */
+
+class FacetTest extends Specification {
+    final static String XML_TEXT = '<installed facet="jst.web" version="2.4"/>'
+
+    def canReadFromXml() {
+        when:
+        Facet facet = new Facet(new XmlParser().parseText(XML_TEXT))
+
+        then:
+        facet == createFacet()
+    }
+
+    def canWriteToXml() {
+        Node rootNode = new Node(null, 'root')
+
+        when:
+        createFacet().appendNode(rootNode)
+
+        then:
+        new Facet(rootNode.installed[0]) == createFacet()
+    }
+
+    def equality() {
+        Facet facet = createFacet()
+        facet.name += 'x'
+
+        expect:
+        facet != createFacet()
+    }
+
+    private Facet createFacet() {
+        return new Facet("jst.web", "2.4")
+    }
+
+
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/JdtTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/JdtTest.groovy
new file mode 100644
index 0000000..606e33c
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/JdtTest.groovy
@@ -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.plugins.ide.eclipse.model
+
+import org.gradle.api.JavaVersion
+import spock.lang.Specification
+
+class JdtTest extends Specification {
+    final Jdt jdt = new Jdt()
+
+    def defaultsForJava1_3Source() {
+        Properties properties = new Properties()
+
+        when:
+        jdt.loadDefaults()
+        jdt.sourceCompatibility = JavaVersion.VERSION_1_3
+        jdt.targetCompatibility = JavaVersion.VERSION_1_3
+        store(properties)
+
+        then:
+        properties['org.eclipse.jdt.core.compiler.compliance'] == '1.3'
+        properties['org.eclipse.jdt.core.compiler.source'] == '1.3'
+        properties['org.eclipse.jdt.core.compiler.problem.assertIdentifier'] == 'ignore'
+        properties['org.eclipse.jdt.core.compiler.problem.enumIdentifier'] == 'ignore'
+        properties['org.eclipse.jdt.core.compiler.codegen.targetPlatform'] == '1.3'
+    }
+
+    def defaultsForJava1_4Source() {
+        Properties properties = new Properties()
+
+        when:
+        jdt.loadDefaults()
+        jdt.sourceCompatibility = JavaVersion.VERSION_1_4
+        jdt.targetCompatibility = JavaVersion.VERSION_1_4
+        store(properties)
+
+        then:
+        properties['org.eclipse.jdt.core.compiler.compliance'] == '1.4'
+        properties['org.eclipse.jdt.core.compiler.source'] == '1.4'
+        properties['org.eclipse.jdt.core.compiler.problem.assertIdentifier'] == 'error'
+        properties['org.eclipse.jdt.core.compiler.problem.enumIdentifier'] == 'warning'
+        properties['org.eclipse.jdt.core.compiler.codegen.targetPlatform'] == '1.4'
+    }
+
+    def defaultsForJava1_5Source() {
+        Properties properties = new Properties()
+
+        when:
+        jdt.loadDefaults()
+        jdt.sourceCompatibility = JavaVersion.VERSION_1_5
+        jdt.targetCompatibility = JavaVersion.VERSION_1_5
+        store(properties)
+
+        then:
+        properties['org.eclipse.jdt.core.compiler.compliance'] == '1.5'
+        properties['org.eclipse.jdt.core.compiler.source'] == '1.5'
+        properties['org.eclipse.jdt.core.compiler.problem.assertIdentifier'] == 'error'
+        properties['org.eclipse.jdt.core.compiler.problem.enumIdentifier'] == 'error'
+        properties['org.eclipse.jdt.core.compiler.codegen.targetPlatform'] == '1.5'
+    }
+
+    def defaultsForJava1_6Source() {
+        Properties properties = new Properties()
+
+        when:
+        jdt.loadDefaults()
+        jdt.sourceCompatibility = JavaVersion.VERSION_1_6
+        jdt.targetCompatibility = JavaVersion.VERSION_1_6
+        store(properties)
+
+        then:
+        properties['org.eclipse.jdt.core.compiler.compliance'] == '1.6'
+        properties['org.eclipse.jdt.core.compiler.source'] == '1.6'
+        properties['org.eclipse.jdt.core.compiler.problem.assertIdentifier'] == 'error'
+        properties['org.eclipse.jdt.core.compiler.problem.enumIdentifier'] == 'error'
+        properties['org.eclipse.jdt.core.compiler.codegen.targetPlatform'] == '1.6'
+    }
+
+    def store(Properties properties) {
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream()
+        jdt.store(outputStream)
+        properties.load(new ByteArrayInputStream(outputStream.toByteArray()))
+    }
+}
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/LibraryTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/LibraryTest.groovy
new file mode 100644
index 0000000..06dd97c
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/LibraryTest.groovy
@@ -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.plugins.ide.eclipse.model
+
+import spock.lang.Specification
+
+/**
+ * @author Hans Dockter
+ */
+
+class LibraryTest extends Specification {
+    final static String XML_TEXT = '''
+                    <classpathentry exported="true" kind="lib" path="/ant.jar" sourcepath="/ant-src.jar">
+                        <attributes>
+                            <attribute name="org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY" value="mynative"/>
+                            <attribute name="javadoc_location" value="jar:file:/ant-javadoc.jar!/path"/>
+                        </attributes>
+                        <accessrules>
+                            <accessrule kind="nonaccessible" pattern="secret**"/>
+                        </accessrules>
+                    </classpathentry>'''
+
+    def canReadFromXml() {
+        when:
+        Library library = new Library(new XmlParser().parseText(XML_TEXT))
+
+        then:
+        library == createLibrary()
+    }
+
+    def canWriteToXml() {
+        Node rootNode = new Node(null, 'root')
+
+        when:
+        createLibrary().appendNode(rootNode)
+
+        then:
+        new Library(rootNode.classpathentry[0]) == createLibrary()
+    }
+
+    def equality() {
+        Library library = createLibrary()
+        library.javadocPath += 'x'
+
+        expect:
+        library != createLibrary()
+    }
+
+    private Library createLibrary() {
+        return new Library('/ant.jar', true, 'mynative', [new AccessRule('nonaccessible', 'secret**')] as Set,
+                "/ant-src.jar", "jar:file:/ant-javadoc.jar!/path")
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/OutputTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/OutputTest.groovy
new file mode 100644
index 0000000..6d5c7c6
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/OutputTest.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.plugins.ide.eclipse.model
+
+import spock.lang.Specification
+
+/**
+ * @author Hans Dockter
+ */
+
+class OutputTest extends Specification {
+    final static String XML_TEXT = '<classpathentry kind="output" path="somePath"/>'
+
+    def canReadFromXml() {
+        when:
+        Output output = new Output(new XmlParser().parseText(XML_TEXT))
+
+        then:
+        output == createOutput()
+    }
+
+    def canWriteToXml() {
+        Node rootNode = new Node(null, 'root')
+
+        when:
+        createOutput().appendNode(rootNode)
+
+        then:
+        new Output(rootNode.classpathentry[0]) == createOutput()
+    }
+
+    def equality() {
+        Output output = createOutput()
+        output.path += 'x'
+
+        expect:
+        output != createOutput()
+    }
+
+    private Output createOutput() {
+        return new Output('somePath')
+    }
+
+
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/ProjectDependencyTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/ProjectDependencyTest.groovy
new file mode 100644
index 0000000..eeb5240
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/ProjectDependencyTest.groovy
@@ -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.plugins.ide.eclipse.model
+
+import spock.lang.Specification
+
+/**
+ * @author Hans Dockter
+ */
+
+class ProjectDependencyTest extends Specification {
+    final static String XML_TEXT = '''
+                <classpathentry kind="src" path="/test2" exported="true">
+                    <attributes>
+                        <attribute name="org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY" value="mynative"/>
+                    </attributes>
+                    <accessrules>
+                        <accessrule kind="nonaccessible" pattern="secret**"/>
+                    </accessrules>
+                </classpathentry>'''
+
+    def canReadFromXml() {
+        when:
+        ProjectDependency projectDependency = new ProjectDependency(new XmlParser().parseText(XML_TEXT))
+
+        then:
+        projectDependency == createProjectDependency()
+    }
+
+    def canWriteToXml() {
+        Node rootNode = new Node(null, 'root')
+
+        when:
+        createProjectDependency().appendNode(rootNode)
+
+        then:
+        new ProjectDependency(rootNode.classpathentry[0]) == createProjectDependency()
+    }
+
+    def equality() {
+        ProjectDependency projectDependency = createProjectDependency()
+        projectDependency.nativeLibraryLocation += 'x'
+
+        expect:
+        projectDependency != createProjectDependency()
+    }
+
+    private ProjectDependency createProjectDependency() {
+        return new ProjectDependency('/test2', true, 'mynative', [new AccessRule('nonaccessible', 'secret**')] as Set, null)
+    }
+
+
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/ProjectTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/ProjectTest.groovy
new file mode 100644
index 0000000..c2ae20b
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/ProjectTest.groovy
@@ -0,0 +1,111 @@
+/*
+ * 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.plugins.ide.eclipse.model;
+
+
+import org.gradle.api.internal.XmlTransformer
+import org.gradle.util.TemporaryFolder
+import org.junit.Rule
+import spock.lang.Specification
+
+/**
+ * @author Hans Dockter
+ */
+public class ProjectTest extends Specification {
+    def static final CUSTOM_REFERENCED_PROJECTS = ['refProject'] as LinkedHashSet
+    def static final CUSTOM_BUILD_COMMANDS = [new BuildCommand('org.eclipse.jdt.core.scalabuilder', [climate: 'cold'])]
+    def static final CUSTOM_NATURES = ['org.eclipse.jdt.core.scalanature'] 
+    def static final CUSTOM_LINKED_RESOURCES = [new Link('somename', 'sometype', 'somelocation', '')] as Set
+
+    @Rule
+    public TemporaryFolder tmpDir = new TemporaryFolder();
+    final Project project = new Project(new XmlTransformer())
+
+    def loadFromReader() {
+        when:
+        project.load(customProjectReader)
+
+        then:
+        project.name == 'test'
+        project.comment == 'for testing'
+        project.referencedProjects == CUSTOM_REFERENCED_PROJECTS
+        project.buildCommands == CUSTOM_BUILD_COMMANDS
+        project.natures == CUSTOM_NATURES
+        project.linkedResources == CUSTOM_LINKED_RESOURCES
+    }
+
+    def configureMergesValues() {
+        EclipseProject eclipseProject = new EclipseProject()
+        eclipseProject.name = 'constructorName'
+        eclipseProject.comment = 'constructorComment'
+        eclipseProject.referencedProjects = ['constructorRefProject'] as LinkedHashSet
+        eclipseProject.buildCommands = [new BuildCommand('constructorbuilder')]
+        eclipseProject.natures = ['constructorNature']
+        eclipseProject.linkedResources = [new Link('constructorName', 'constructorType', 'constructorLocation', '')] as Set
+
+        when:
+        project.load(customProjectReader)
+        project.configure(eclipseProject)
+
+        then:
+        project.name == eclipseProject.name
+        project.comment == eclipseProject.comment
+        project.referencedProjects == eclipseProject.referencedProjects + CUSTOM_REFERENCED_PROJECTS
+        project.buildCommands == CUSTOM_BUILD_COMMANDS + eclipseProject.buildCommands
+        project.natures == CUSTOM_NATURES + eclipseProject.natures
+        project.linkedResources == eclipseProject.linkedResources + CUSTOM_LINKED_RESOURCES
+    }
+
+    def loadDefaults() {
+        when:
+        project.loadDefaults()
+
+        then:
+        project.name == ""
+        project.comment == ""
+        project.referencedProjects == [] as Set
+        project.buildCommands == []
+        project.natures == []
+        project.linkedResources == [] as Set
+    }
+
+    def toXml_shouldContainCustomValues() {
+        EclipseProject eclipseProject = new EclipseProject()
+        eclipseProject.name = 'constructorName'
+        eclipseProject.comment = 'constructorComment'
+        eclipseProject.referencedProjects = ['constructorRefProject'] as LinkedHashSet
+
+        when:
+        project.load(customProjectReader)
+        project.configure(eclipseProject)
+        def xml = getToXmlReader()
+        def other = new Project(new XmlTransformer())
+        other.load(xml)
+
+        then:
+        project == other
+    }
+
+    private InputStream getCustomProjectReader() {
+        return getClass().getResourceAsStream('customProject.xml')
+    }
+
+    private InputStream getToXmlReader() {
+        ByteArrayOutputStream toXmlText = new ByteArrayOutputStream()
+        project.store(toXmlText)
+        return new ByteArrayInputStream(toXmlText.toByteArray())
+    }
+}
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/SourceFolderTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/SourceFolderTest.groovy
new file mode 100644
index 0000000..ed2aac4
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/SourceFolderTest.groovy
@@ -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.plugins.ide.eclipse.model
+
+import spock.lang.Specification
+
+/**
+ * @author Hans Dockter
+ */
+class SourceFolderTest extends Specification {
+    final static String XML_TEXT = '''
+                <classpathentry including="**/Test1*|**/Test2*" excluding="**/Test3*|**/Test4*" kind="src" output="bin2" path="src">
+                    <attributes>
+                        <attribute name="org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY" value="mynative"/>
+                    </attributes>
+                    <accessrules>
+                        <accessrule kind="nonaccessible" pattern="secret**"/>
+                    </accessrules>
+                </classpathentry>'''
+
+    def canReadFromXml() {
+        expect:
+        new SourceFolder(new XmlParser().parseText(XML_TEXT)) == createSourceFolder()
+    }
+
+    def canWriteToXml() {
+        Node rootNode = new Node(null, 'root')
+
+        when:
+        createSourceFolder().appendNode(rootNode)
+
+        then:
+        new SourceFolder(rootNode.classpathentry[0]) == createSourceFolder()
+    }
+
+    def equality() {
+        SourceFolder sourceFolder = createSourceFolder()
+        sourceFolder.nativeLibraryLocation += 'x'
+
+        expect:
+        sourceFolder != createSourceFolder()
+    }
+
+    def createSourceFolder() {
+        return new SourceFolder('src', 'mynative', [new AccessRule('nonaccessible', 'secret**')] as Set,
+            'bin2', ['**/Test1*' ,'**/Test2*'], ['**/Test3*' ,'**/Test4*'])
+    }
+}
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/VariableTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/VariableTest.groovy
new file mode 100644
index 0000000..1310e0d
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/VariableTest.groovy
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.eclipse.model
+
+import spock.lang.Specification
+
+/**
+ * @author Hans Dockter
+ */
+
+class VariableTest extends Specification {
+    final static String XML_TEXT = '''
+                <classpathentry exported="true" kind="var" path="/GRADLE_CACHE/ant.jar" sourcepath="/GRADLE_CACHE/ant-src.jar">
+                    <attributes>
+                        <attribute name="org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY" value="mynative"/>
+                        <attribute name="javadoc_location" value="jar:file:/GRADLE_CACHE/ant-javadoc.jar!/path"/>
+                    </attributes>
+                    <accessrules>
+                        <accessrule kind="nonaccessible" pattern="secret**"/>
+                    </accessrules>
+                </classpathentry>'''
+
+    def canReadFromXml() {
+        when:
+        Variable variable = new Variable(new XmlParser().parseText(XML_TEXT))
+
+        then:
+        variable == createVariable()
+    }
+
+    def canWriteToXml() {
+        Node rootNode = new Node(null, 'root')
+
+        when:
+        createVariable().appendNode(rootNode)
+
+        then:
+        new Variable(rootNode.classpathentry[0]) == createVariable()
+    }
+
+    def equality() {
+        Variable variable = createVariable()
+        variable.sourcePath += 'x'
+
+        expect:
+        variable != createVariable()
+    }
+
+    private Variable createVariable() {
+        return new Variable('/GRADLE_CACHE/ant.jar', true, 'mynative', [new AccessRule('nonaccessible', 'secret**')] as Set,
+                "/GRADLE_CACHE/ant-src.jar", "jar:file:/GRADLE_CACHE/ant-javadoc.jar!/path")
+    }
+
+
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/WbDependentModuleTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/WbDependentModuleTest.groovy
new file mode 100644
index 0000000..82ea561
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/WbDependentModuleTest.groovy
@@ -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.plugins.ide.eclipse.model
+
+import spock.lang.Specification
+
+/**
+ * @author Hans Dockter
+ */
+
+class WbDependentModuleTest extends Specification {
+    final static String XML_TEXT = '''
+                <dependent-module deploy-path="/WEB-INF/lib" handle="module:/classpath/gradle-1.0.0.jar">
+                    <dependency-type>uses</dependency-type>
+                </dependent-module>'''
+
+    def canReadFromXml() {
+        when:
+        WbDependentModule wbDependentModule = new WbDependentModule(new XmlParser().parseText(XML_TEXT))
+
+        then:
+        wbDependentModule == createWbDependentModule()
+    }
+
+    def canWriteToXml() {
+        Node rootNode = new Node(null, 'root')
+
+        when:
+        createWbDependentModule().appendNode(rootNode)
+
+        then:
+        new WbDependentModule(rootNode.'dependent-module'[0]) == createWbDependentModule()
+    }
+
+    def equality() {
+        WbDependentModule wbDependentModule = createWbDependentModule()
+        wbDependentModule.handle += 'x'
+
+        expect:
+        wbDependentModule != createWbDependentModule()
+    }
+
+    private WbDependentModule createWbDependentModule() {
+        return new WbDependentModule("/WEB-INF/lib", "module:/classpath/gradle-1.0.0.jar")
+    }
+
+
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/WbPropertyTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/WbPropertyTest.groovy
new file mode 100644
index 0000000..cbdd953
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/WbPropertyTest.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.plugins.ide.eclipse.model
+
+import spock.lang.Specification
+
+/**
+ * @author Hans Dockter
+ */
+
+class WbPropertyTest extends Specification {
+    final static String XML_TEXT = '<property name="java-output-path" value="/build/classes"/>'
+
+    def canReadFromXml() {
+        when:
+        WbProperty wbProperty = new WbProperty(new XmlParser().parseText(XML_TEXT))
+
+        then:
+        wbProperty == createWbProperty()
+    }
+
+    def canWriteToXml() {
+        Node rootNode = new Node(null, 'root')
+
+        when:
+        createWbProperty().appendNode(rootNode)
+
+        then:
+        new WbProperty(rootNode.property[0]) == createWbProperty()
+    }
+
+    def equality() {
+        WbProperty wbProperty = createWbProperty()
+        wbProperty.name += 'x'
+
+        expect:
+        wbProperty != createWbProperty()
+    }
+
+    private WbProperty createWbProperty() {
+        return new WbProperty("java-output-path", "/build/classes")
+    }
+
+
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/WbResourceTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/WbResourceTest.groovy
new file mode 100644
index 0000000..12dec70
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/WbResourceTest.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.plugins.ide.eclipse.model
+
+import spock.lang.Specification
+
+/**
+ * @author Hans Dockter
+ */
+
+class WbResourceTest extends Specification {
+    final static String XML_TEXT = '<wb-resource deploy-path="/" source-path="src/main/webapp"/>'
+
+    def canReadFromXml() {
+        when:
+        WbResource wbResource = new WbResource(new XmlParser().parseText(XML_TEXT))
+
+        then:
+        wbResource == createWbResource()
+    }
+
+    def canWriteToXml() {
+        Node rootNode = new Node(null, 'root')
+
+        when:
+        createWbResource().appendNode(rootNode)
+
+        then:
+        new WbResource(rootNode.'wb-resource'[0]) == createWbResource()
+    }
+
+    def equality() {
+        WbResource wbResource = createWbResource()
+        wbResource.sourcePath += 'x'
+
+        expect:
+        wbResource != createWbResource()
+    }
+
+    private WbResource createWbResource() {
+        return new WbResource("/", "src/main/webapp")
+    }
+
+
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/WtpComponentTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/WtpComponentTest.groovy
new file mode 100644
index 0000000..fd0302e
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/WtpComponentTest.groovy
@@ -0,0 +1,91 @@
+/*
+ * 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.plugins.ide.eclipse.model
+
+import org.custommonkey.xmlunit.XMLUnit
+import org.gradle.api.internal.XmlTransformer
+import org.gradle.util.TemporaryFolder
+import org.junit.Rule
+import spock.lang.Specification
+
+/**
+ * @author Hans Dockter
+ */
+public class WtpComponentTest extends Specification {
+    private static final List CUSTOM_WB_MODULE_ENTRIES = [
+            new WbDependentModule('/WEB-INF/lib', "module:/classpath/myapp-1.0.0.jar"),
+            new WbResource("/WEB-INF/classes", "src/main/java")]
+
+    private final WtpComponent component = new WtpComponent(new XmlTransformer())
+
+    @Rule
+    public TemporaryFolder tmpDir = new TemporaryFolder()
+
+    def "load existing XML file"() {
+        when:
+        component.load(customComponentReader)
+
+        then:
+        component.deployName == 'recu'
+        component.contextPath == 'recu'
+        component.wbModuleEntries == CUSTOM_WB_MODULE_ENTRIES
+    }
+
+    def "merge existing and new configuration"() {
+        def constructorDeployName = 'build'
+        def constructorContextPath = 'context'
+        def constructorWbModuleEntries = [createSomeWbModuleEntry()]
+
+        when:
+        component.load(customComponentReader)
+        component.configure(constructorDeployName, constructorContextPath, constructorWbModuleEntries + [CUSTOM_WB_MODULE_ENTRIES[0]])
+
+        then:
+        component.wbModuleEntries == CUSTOM_WB_MODULE_ENTRIES + constructorWbModuleEntries
+        component.deployName == constructorDeployName
+        component.contextPath == constructorContextPath
+    }
+
+    def "load defaults"() {
+        when:
+        component.loadDefaults()
+
+        then:
+        component.xml != null
+        component.wbModuleEntries == []
+        component.deployName == null
+        component.contextPath == null
+    }
+
+    def "roundtripping the component file leaves it unchanged"() {
+        when:
+        component.load(customComponentReader)
+        def roundTripped = tmpDir.file("component.xml")
+        component.store(roundTripped)
+
+        then:
+        XMLUnit.compareXML(customComponentReader.text, roundTripped.text).identical()
+
+    }
+
+    private InputStream getCustomComponentReader() {
+        getClass().getResourceAsStream('customOrgEclipseWstCommonComponent.xml')
+    }
+
+    private WbProperty createSomeWbModuleEntry() {
+        return new WbProperty('someProp', 'someValue')
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/WtpFacetTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/WtpFacetTest.groovy
new file mode 100644
index 0000000..cf7a408
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/WtpFacetTest.groovy
@@ -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.plugins.ide.eclipse.model
+
+import org.custommonkey.xmlunit.XMLUnit
+import org.gradle.api.internal.XmlTransformer
+import org.gradle.util.TemporaryFolder
+import org.junit.Rule
+import spock.lang.Specification
+
+/**
+ * @author Hans Dockter
+ */
+public class WtpFacetTest extends Specification {
+    private static final List CUSTOM_FACETS = [new Facet('jst.web', '2.4'), new Facet('jst.java', '1.4')]
+
+    private final WtpFacet facet = new WtpFacet(new XmlTransformer())
+
+    @Rule
+    public TemporaryFolder tmpDir = new TemporaryFolder()
+
+    def "load existing XML file"() {
+        when:
+        facet.load(customFacetReader)
+
+        then:
+        facet.facets == CUSTOM_FACETS
+    }
+
+    def "merge existing and newly added facets"() {
+        def constructorFacets = [createSomeFacet()]
+
+        when:
+        facet.load(customFacetReader)
+        facet.configure(constructorFacets + [CUSTOM_FACETS[0]])
+
+        then:
+        facet.facets == CUSTOM_FACETS + constructorFacets
+    }
+
+    def "load defaults"() {
+        when:
+        facet.loadDefaults()
+
+        then:
+        facet.xml != null
+        facet.facets == []
+    }
+
+    def "roundtripping the facet file leaves it unchanged"() {
+        when:
+        facet.load(customFacetReader)
+        def roundTripped = tmpDir.file("facet.xml")
+        facet.store(roundTripped)
+
+        then:
+        XMLUnit.compareXML(customFacetReader.text, roundTripped.text).identical()
+    }
+
+    private InputStream getCustomFacetReader() {
+        getClass().getResourceAsStream('customOrgEclipseWstCommonProjectFacetCoreXml.xml')
+    }
+
+    private Facet createSomeFacet() {
+        new Facet('someName', '1.0.0')
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/internal/ProjectDependencyBuilderTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/internal/ProjectDependencyBuilderTest.groovy
new file mode 100644
index 0000000..7a48887
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/internal/ProjectDependencyBuilderTest.groovy
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.eclipse.model.internal
+
+import spock.lang.Specification
+
+/**
+ * @author Szczepan Faber, @date: 11.03.11
+ */
+class ProjectDependencyBuilderTest extends Specification {
+
+    def ProjectDependencyBuilder builder = new ProjectDependencyBuilder()
+
+    static class ProjectStub {
+        String name
+        String path
+        GenerateEclipseProjectStub eclipseProject
+    }
+
+    static class GenerateEclipseProjectStub {
+        String projectName
+    }
+
+    def "should create dependency using project name"() {
+        given:
+        def project = new ProjectStub(name: 'coolProject')
+
+        when:
+        def dependency = builder.build(project)
+
+        then:
+        dependency.path == '/coolProject'
+    }
+
+    def "should create dependency using eclipse projectName"() {
+        given:
+        def eclipseProject = new GenerateEclipseProjectStub(projectName: 'eclipse-project')
+        def project = new ProjectStub(eclipseProject: eclipseProject)
+
+        when:
+        def dependency = builder.build(project)
+
+        then:
+        dependency.path == '/eclipse-project'
+    }
+}
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/ GenerateIdeaModuleTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/ GenerateIdeaModuleTest.groovy
new file mode 100644
index 0000000..615faaf
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/ GenerateIdeaModuleTest.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.plugins.ide.idea
+
+import org.gradle.api.Project
+import org.gradle.api.internal.project.DefaultProject
+import org.gradle.util.HelperUtil
+import spock.lang.Specification
+
+/**
+ * @author Szczepan Faber, @date 03.03.11
+ */
+class GenerateIdeaModuleTest extends Specification {
+
+    DefaultProject project = HelperUtil.createRootProject()
+    Project childProject = HelperUtil.createChildProject(project, "child", new File("."))
+    Project grandChildProject = HelperUtil.createChildProject(childProject, "grandChild", new File("."))
+    IdeaPlugin ideaPlugin = new IdeaPlugin()
+
+    def "moduleName controls outputFile"() {
+        given:
+        applyPluginToProjects()
+        assert childProject.ideaModule.moduleName == "child"
+        def existingOutputFolder = childProject.ideaModule.outputFile.parentFile
+
+        when:
+        childProject.ideaModule.moduleName = "foo"
+
+        then:
+        childProject.ideaModule.moduleName == "foo"
+        childProject.ideaModule.outputFile.name == "foo.iml"
+        childProject.ideaModule.outputFile.parentFile == existingOutputFolder
+    }
+
+    private applyPluginToProjects() {
+        ideaPlugin.apply(project)
+        ideaPlugin.apply(childProject)
+        ideaPlugin.apply(grandChildProject)
+    }
+}
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/IdeaPluginTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/IdeaPluginTest.groovy
new file mode 100644
index 0000000..2e7f0ea
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/IdeaPluginTest.groovy
@@ -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.plugins.ide.idea
+
+import org.gradle.api.JavaVersion
+import org.gradle.api.Project
+import org.gradle.api.Task
+import org.gradle.api.internal.project.DefaultProject
+import org.gradle.api.tasks.Delete
+import org.gradle.util.HelperUtil
+import spock.lang.Specification
+
+/**
+ * @author Hans Dockter
+ */
+class IdeaPluginTest extends Specification {
+    private final DefaultProject project = HelperUtil.createRootProject()
+    private final Project childProject = HelperUtil.createChildProject(project, "child", new File("."))
+    private final IdeaPlugin ideaPlugin = new IdeaPlugin()
+
+    def "adds 'ideaProject' task to root project"() {
+        when:
+        applyPluginToProjects()
+
+        then:
+        assertThatCleanIdeaDependsOnDeleteTask(project, project.cleanIdeaProject)
+        GenerateIdeaProject ideaProjectTask = project.ideaProject
+        ideaProjectTask instanceof GenerateIdeaProject
+        ideaProjectTask.outputFile == new File(project.projectDir, project.name + ".ipr")
+        ideaProjectTask.subprojects == project.rootProject.allprojects
+        ideaProjectTask.javaVersion == JavaVersion.VERSION_1_6.toString()
+        ideaProjectTask.wildcards == ['!?*.java', '!?*.groovy'] as Set
+
+        childProject.tasks.findByName('ideaProject') == null
+        childProject.tasks.findByName('cleanIdeaProject') == null
+    }
+
+    def "adds 'ideaWorkspace' task to root project"() {
+        when:
+        applyPluginToProjects()
+
+        then:
+        project.ideaWorkspace instanceof GenerateIdeaWorkspace
+        assertThatCleanIdeaDependsOnDeleteTask(project, project.cleanIdeaWorkspace)
+
+        childProject.tasks.findByName('ideaWorkspace') == null
+        childProject.tasks.findByName('cleanIdeaWorkspace') == null
+    }
+
+    def "adds 'ideaModule' task to projects"() {
+        when:
+        applyPluginToProjects()
+
+        then:
+        assertThatIdeaModuleIsProperlyConfigured(project)
+        assertThatIdeaModuleIsProperlyConfigured(childProject)
+    }
+
+    def "adds special configuration if Java plugin is applied"() {
+        when:
+        applyPluginToProjects()
+        project.apply(plugin: 'java')
+
+        then:
+        project.ideaProject.javaVersion == project.sourceCompatibility.toString()
+
+        GenerateIdeaModule ideaModuleTask = project.ideaModule
+        ideaModuleTask.sourceDirs == project.sourceSets.main.allSource.srcDirs
+        ideaModuleTask.testSourceDirs == project.sourceSets.test.allSource.srcDirs
+        def configurations = project.configurations
+        ideaModuleTask.scopes == [
+                COMPILE: [plus: [configurations.compile], minus: []],
+                RUNTIME: [plus: [configurations.runtime], minus: [configurations.compile]],
+                TEST: [plus: [configurations.testRuntime], minus: [configurations.runtime]],
+                PROVIDED: [plus: [], minus: []]
+        ]
+    }
+
+    def "picks up late changes to build dir"() {
+        when:
+        applyPluginToProjects()
+        project.apply(plugin: 'java')
+        project.buildDir = project.file('target')
+
+        then:
+        project.ideaModule.excludeDirs == [project.buildDir, project.file('.gradle')] as Set
+    }
+
+    def "adds 'cleanIdea' task to projects"() {
+        when:
+        applyPluginToProjects()
+
+        then:
+        project.cleanIdea instanceof Task
+        childProject.cleanIdea instanceof Task
+    }
+
+    private void assertThatIdeaModuleIsProperlyConfigured(Project project) {
+        GenerateIdeaModule ideaModuleTask = project.ideaModule
+        assert ideaModuleTask instanceof GenerateIdeaModule
+        assert ideaModuleTask.outputFile == new File(project.projectDir, project.name + ".iml")
+        assert ideaModuleTask.moduleDir == project.projectDir
+        assert ideaModuleTask.sourceDirs == [] as Set
+        assert ideaModuleTask.testSourceDirs == [] as Set
+        assert ideaModuleTask.excludeDirs == [project.buildDir, project.file('.gradle')] as Set
+        assert ideaModuleTask.variables == [:]
+        assertThatCleanIdeaDependsOnDeleteTask(project, project.cleanIdeaModule)
+    }
+
+    private void assertThatCleanIdeaDependsOnDeleteTask(Project project, Task dependsOnTask) {
+        assert dependsOnTask instanceof Delete
+        assert project.cleanIdea.taskDependencies.getDependencies(project.cleanIdea).contains(dependsOnTask)
+    }
+
+    private applyPluginToProjects() {
+        ideaPlugin.apply(project)
+        ideaPlugin.apply(childProject)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/ModuleDependencyTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/ModuleDependencyTest.groovy
new file mode 100644
index 0000000..da03e65
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/ModuleDependencyTest.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.plugins.ide.idea.model
+
+import spock.lang.Specification
+
+/**
+ * @author Hans Dockter
+ */
+class ModuleDependencyTest extends Specification {
+    def equality() {
+        expect:
+        new ModuleDependency("a", null).equals(new ModuleDependency("a", "COMPILE"))
+        new ModuleDependency("a", "").equals(new ModuleDependency("a", "COMPILE"))
+        new ModuleDependency("a", "COMPILE").equals(new ModuleDependency("a", ""))
+        new ModuleDependency("a", "COMPILE").equals(new ModuleDependency("a", null))
+        new ModuleDependency("a", "").equals(new ModuleDependency("a", ""))
+        !new ModuleDependency("a", "").equals(new ModuleDependency("b", ""))
+        !new ModuleDependency("a", "RUNTIME").equals(new ModuleDependency("a", "COMPILE"))
+        !new ModuleDependency("a", "").equals(new ModuleDependency("a", "RUNTIME"))
+        !new ModuleDependency("a", "RUNTIME").equals(new ModuleDependency("a", ""))
+    }
+
+    def hash() {
+        expect:
+        new ModuleDependency("a", null).hashCode() == new ModuleDependency("a", "COMPILE").hashCode()
+        new ModuleDependency("a", "").hashCode() == new ModuleDependency("a", "COMPILE").hashCode()
+    }
+
+    def shouldExportForCompileAndRuntimeScope() {
+        expect:
+        new ModuleDependency("a", "COMPILE").exported
+        new ModuleDependency("a", "RUNTIME").exported
+        new ModuleDependency("a", "").exported
+        new ModuleDependency("a", null).exported
+        !(new ModuleDependency("a", "TEST").exported)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/ModuleLibraryTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/ModuleLibraryTest.groovy
new file mode 100644
index 0000000..4019a41
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/ModuleLibraryTest.groovy
@@ -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.plugins.ide.idea.model
+
+import spock.lang.Specification
+
+/**
+ * @author Hans Dockter
+ */
+class ModuleLibraryTest extends Specification {
+    def equality() {
+        expect:
+        createModuleLibraryWithScope(null).equals(createModuleLibraryWithScope("COMPILE"))
+        createModuleLibraryWithScope("").equals(createModuleLibraryWithScope("COMPILE"))
+        createModuleLibraryWithScope("COMPILE").equals(createModuleLibraryWithScope(null))
+        createModuleLibraryWithScope("COMPILE").equals(createModuleLibraryWithScope(""))
+        createModuleLibraryWithScope("").equals(createModuleLibraryWithScope(""))
+        !createModuleLibraryWithScope("RUNTIME").equals(createModuleLibraryWithScope("COMPILE"))
+        !createModuleLibraryWithScope("").equals(createModuleLibraryWithScope("RUNTIME"))
+        !createModuleLibraryWithScope("RUNTIME").equals(createModuleLibraryWithScope(""))
+    }
+
+    def hash() {
+        expect:
+        createModuleLibraryWithScope(null).hashCode() == createModuleLibraryWithScope("COMPILE").hashCode()
+        createModuleLibraryWithScope("").hashCode() == createModuleLibraryWithScope("COMPILE").hashCode()
+    }
+
+    private ModuleLibrary createModuleLibraryWithScope(String scope) {
+        new ModuleLibrary([] as Set, [] as Set, [] as Set, [] as Set, scope)
+    }
+
+    def shouldExportForCompileAndRuntimeScope() {
+        expect:
+        createModuleLibraryWithScope("COMPILE").exported
+        createModuleLibraryWithScope("RUNTIME").exported
+        createModuleLibraryWithScope("").exported
+        createModuleLibraryWithScope(null).exported
+        !(createModuleLibraryWithScope("TEST").exported)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/ModulePathTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/ModulePathTest.groovy
new file mode 100644
index 0000000..74afcf4
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/ModulePathTest.groovy
@@ -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.plugins.ide.idea.model
+
+import org.gradle.util.Matchers
+import spock.lang.Specification
+
+class ModulePathTest extends Specification {
+    def pathsAreEqualWhenTheirPathAndFilePathAreEqual() {
+        Path path = new Path('url')
+        String filePath = 'path'
+
+        expect:
+        Matchers.strictlyEquals(new ModulePath(path, filePath), new ModulePath(path, filePath))
+        new ModulePath(path, filePath) != new ModulePath(path, 'other')
+        new ModulePath(path, filePath) != new ModulePath(new Path('other'), filePath)
+    }
+}
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/ModuleTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/ModuleTest.groovy
new file mode 100644
index 0000000..7add8a6
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/ModuleTest.groovy
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.idea.model
+
+import org.gradle.api.internal.XmlTransformer
+import spock.lang.Specification
+
+/**
+ * @author Hans Dockter
+ */
+class ModuleTest extends Specification {
+    final PathFactory pathFactory = new PathFactory()
+    final XmlTransformer xmlTransformer = new XmlTransformer()
+    final customSourceFolders = [path('file://$MODULE_DIR$/src')] as LinkedHashSet
+    final customTestSourceFolders = [path('file://$MODULE_DIR$/srcTest')] as LinkedHashSet
+    final customExcludeFolders = [path('file://$MODULE_DIR$/target')] as LinkedHashSet
+    final customDependencies = [
+            new ModuleLibrary([path('file://$MODULE_DIR$/gradle/lib')] as Set,
+                    [path('file://$MODULE_DIR$/gradle/javadoc')] as Set, [path('file://$MODULE_DIR$/gradle/src')] as Set,
+                    [] as Set, null),
+            new ModuleLibrary([path('file://$MODULE_DIR$/ant/lib'), path('jar://$GRADLE_CACHE$/gradle.jar!/')] as Set, [] as Set, [] as Set,
+                    [new JarDirectory(path('file://$MODULE_DIR$/ant/lib'), false)] as Set, "RUNTIME"),
+            new ModuleDependency('someModule', null)]
+
+    Module module = new Module(xmlTransformer, pathFactory)
+
+    def loadFromReader() {
+        when:
+        module.load(customModuleReader)
+
+        then:
+        module.javaVersion == "1.6"
+        module.sourceFolders == customSourceFolders
+        module.testSourceFolders == customTestSourceFolders
+        module.excludeFolders == customExcludeFolders
+        module.outputDir == path('file://$MODULE_DIR$/out')
+        module.testOutputDir == path('file://$MODULE_DIR$/outTest')
+        (module.dependencies as List) == customDependencies
+    }
+
+    def configureOverwritesDependenciesAndAppendsAllOtherEntries() {
+        def constructorSourceFolders = [path('a')] as Set
+        def constructorTestSourceFolders = [path('b')] as Set
+        def constructorExcludeFolders = [path('c')] as Set
+        def constructorInheritOutputDirs = false
+        def constructorOutputDir = path('someOut')
+        def constructorJavaVersion = '1.6'
+        def constructorTestOutputDir = path('someTestOut')
+        def constructorModuleDependencies = [
+                customDependencies[0],
+                new ModuleLibrary([path('x')], [], [], [new JarDirectory(path('y'), false)], null)] as LinkedHashSet
+
+        when:
+        module.load(customModuleReader)
+        module.configure(null, constructorSourceFolders, constructorTestSourceFolders, constructorExcludeFolders,
+                constructorInheritOutputDirs, constructorOutputDir, constructorTestOutputDir, constructorModuleDependencies, constructorJavaVersion)
+
+        then:
+        module.sourceFolders == customSourceFolders + constructorSourceFolders
+        module.testSourceFolders == customTestSourceFolders + constructorTestSourceFolders
+        module.excludeFolders == customExcludeFolders + constructorExcludeFolders
+        module.outputDir == constructorOutputDir
+        module.testOutputDir == constructorTestOutputDir
+        module.javaVersion == constructorJavaVersion
+        module.dependencies == constructorModuleDependencies
+    }
+
+    def loadDefaults() {
+        when:
+        module.loadDefaults()
+
+        then:
+        module.javaVersion == Module.INHERITED
+        module.inheritOutputDirs
+        module.sourceFolders == [] as Set
+        module.dependencies.size() == 0
+    }
+
+    def generatedXmlShouldContainCustomValues() {
+        def constructorSourceFolders = [new Path('a')] as Set
+        def constructorOutputDir = new Path('someOut')
+        def constructorTestOutputDir = new Path('someTestOut')
+
+        when:
+        module.loadDefaults()
+        module.configure(null, constructorSourceFolders, [] as Set, [] as Set, false, constructorOutputDir, constructorTestOutputDir, [] as Set, null)
+        def xml = toXmlReader
+        def newModule = new Module(xmlTransformer, pathFactory)
+        newModule.load(xml)
+
+        then:
+        this.module == newModule
+    }
+
+    private InputStream getToXmlReader() {
+        ByteArrayOutputStream toXmlText = new ByteArrayOutputStream()
+        module.store(toXmlText)
+        return new ByteArrayInputStream(toXmlText.toByteArray())
+    }
+
+    private InputStream getCustomModuleReader() {
+        return getClass().getResourceAsStream('customModule.xml')
+    }
+
+    private Path path(String url) {
+        pathFactory.path(url)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/PathFactoryTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/PathFactoryTest.groovy
new file mode 100644
index 0000000..04244b8
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/PathFactoryTest.groovy
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.idea.model
+
+import org.gradle.util.TemporaryFolder
+import org.gradle.util.TestFile
+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 = relpath(file)
+
+        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 rootDir = factory.path(tmpDir.dir)
+        rootDir.url == 'file://$ROOT_DIR$/'
+
+        def subDir = factory.path(tmpDir.file('sub'))
+        subDir.url == 'file://$SUB_DIR$/'
+    }
+
+    def createsPathForAJarFile() {
+        factory.addPathVariable('ROOT_DIR', tmpDir.dir)
+
+        expect:
+        def path = factory.path(tmpDir.file('a.jar'))
+        path.url == 'jar://$ROOT_DIR$/a.jar!/'
+    }
+
+    def createsRelativePath() {
+        factory.addPathVariable('ROOT_DIR', tmpDir.dir)
+
+        expect:
+        def path = factory.relativePath('ROOT_DIR', tmpDir.file('a/b'))
+        path.url == 'file://$ROOT_DIR$/a/b'
+
+        def parentPath = factory.relativePath('ROOT_DIR', tmpDir.dir.parentFile.parentFile.file('a/b'))
+        parentPath.url == 'file://$ROOT_DIR$/../../a/b'
+    }
+    
+    def createsPathForAFileUrl() {
+        expect:
+        def path = factory.path('file://a/b/c')
+        path.url == 'file://a/b/c'
+    }
+
+    def createsPathForAJarUrl() {
+        expect:
+        def path = factory.path('jar://a/b/c.jar!/some/entry')
+        path.url == 'jar://a/b/c.jar!/some/entry'
+    }
+
+    def createsPathForAUrlWithUnknownScheme() {
+        expect:
+        def path = factory.path('other:abc')
+        path.url == 'other:abc'
+    }
+
+    def createsPathForAUrlWithPathVariables() {
+        factory.addPathVariable('ROOT_DIR', tmpDir.dir)
+
+        expect:
+        def path = factory.path('file://$ROOT_DIR$/c')
+        path.url == 'file://$ROOT_DIR$/c'
+    }
+
+    def filePathsAreEqualWhenTheyPointToTheSameFile() {
+        TestFile subDir = tmpDir.file('sub')
+        TestFile childFile = tmpDir.file('sub/a/b')
+
+        factory.addPathVariable('SUB_DIR', subDir)
+        factory.addPathVariable('ROOT_DIR', tmpDir.dir)
+
+        expect:
+
+        // Using files
+        factory.path(subDir) == factory.path(subDir)
+        factory.path(childFile) == factory.path(childFile)
+        factory.path(childFile) != factory.path(subDir)
+
+        // Using normalised absolute urls
+        factory.path(subDir) == factory.path("file://${relpath(subDir)}")
+        factory.path(subDir) == factory.path("file://${relpath(childFile)}/../..")
+        factory.path("file://${relpath(subDir)}") != factory.path("file://${relpath(childFile)}")
+
+        // Using absolute paths
+        factory.path(subDir) == factory.path("file://${subDir.absolutePath}")
+
+        // Using replacement variables
+        factory.path(childFile) == factory.path('file://$SUB_DIR$/a/b')
+        factory.path(childFile) == factory.path('file://$SUB_DIR$/c/../a/b')
+        factory.path('file://$ROOT_DIR$/sub') == factory.path('file://$SUB_DIR$')
+        factory.path('file://$ROOT_DIR$') != factory.path('file://$SUB_DIR$')
+    }
+
+    def filePathsAreEqualWhenTheyPointToTheSameEntryInTheSameFile() {
+        TestFile subDir = tmpDir.file('sub')
+        TestFile childFile = tmpDir.file('sub/a/b.jar')
+
+        factory.addPathVariable('SUB_DIR', subDir)
+        factory.addPathVariable('ROOT_DIR', tmpDir.dir)
+
+        expect:
+
+        // Using files
+        factory.path(childFile) == factory.path(childFile)
+        factory.path(childFile) != factory.path(subDir)
+
+        // Using normalised absolute urls
+        factory.path(childFile) == factory.path("jar://${relpath(childFile)}!/")
+        factory.path("jar://${relpath(childFile)}!/entry") == factory.path("jar://${relpath(childFile)}!/entry")
+        factory.path(childFile) != factory.path("jar://${relpath(childFile)}!/entry")
+
+        // Using replacement variables
+        factory.path(childFile) == factory.path('jar://$SUB_DIR$/a/b.jar!/')
+        factory.path(childFile) == factory.path('jar://$SUB_DIR$/c/../a/b.jar!/')
+
+        factory.path(childFile) != factory.path('jar://$SUB_DIR$/a/b.jar')
+        factory.path(childFile) != factory.path("file://${relpath(childFile)}")
+    }
+
+    private String relpath(File file) {
+        return file.absolutePath.replace(File.separator, '/')
+    }
+
+}
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/PathTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/PathTest.groovy
new file mode 100644
index 0000000..03ce245
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/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.ide.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 pathsAreEqualWhenTheyHaveTheSameCanonicalUrl() {
+        expect:
+        Matchers.strictlyEquals(new Path('file://$ROOT_DIR$/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/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/ProjectTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/ProjectTest.groovy
new file mode 100644
index 0000000..6d1bebe
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/ProjectTest.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.plugins.ide.idea.model
+
+import org.gradle.api.internal.XmlTransformer
+import spock.lang.Specification
+
+/**
+ * @author Hans Dockter
+ */
+class ProjectTest extends Specification {
+    final PathFactory pathFactory = new PathFactory()
+    final customModules = [new ModulePath(path('file://$PROJECT_DIR$/gradle-idea-plugin.iml'), '$PROJECT_DIR$/gradle-idea-plugin.iml')] as Set
+    final customWildcards = ["?*.gradle", "?*.grails"] as Set
+    Project project = new Project(new XmlTransformer(), pathFactory)
+
+    def loadFromReader() {
+        when:
+        project.load(customProjectReader)
+
+        then:
+        project.modulePaths == customModules
+        project.wildcards == customWildcards
+        project.jdk == new Jdk(true, false, null, "1.4")
+    }
+
+    def customJdkAndWildcards_shouldBeMerged() {
+        def modules = [new ModulePath(path('file://$PROJECT_DIR$/other.iml'), '$PROJECT_DIR$/other.iml')] as Set
+
+        when:
+        project.load(customProjectReader)
+        project.configure(modules, '1.6', ['?*.groovy'] as Set)
+
+        then:
+        project.modulePaths == customModules + modules
+        project.wildcards == customWildcards + ['?*.groovy'] as Set
+        project.jdk == new Jdk("1.6")
+    }
+
+    def loadDefaults() {
+        when:
+        project.loadDefaults()
+
+        then:
+        project.modulePaths.size() == 0
+        project.wildcards == [] as Set
+        project.jdk == new Jdk(true, true, "JDK_1_5", null)
+    }
+
+    def toXml_shouldContainCustomValues() {
+        when:
+        project.loadDefaults()
+        project.configure([] as Set, '1.5', ['?*.groovy'] as Set)
+        def xml = toXmlReader
+        def other = new Project(new XmlTransformer(), pathFactory)
+        other.load(xml)
+
+        then:
+        project.wildcards == other.wildcards
+        project.modulePaths == other.modulePaths
+        project.jdk == other.jdk
+    }
+
+    private InputStream getToXmlReader() {
+        ByteArrayOutputStream toXmlText = new ByteArrayOutputStream()
+        project.store(toXmlText)
+        return new ByteArrayInputStream(toXmlText.toByteArray())
+    }
+
+    private InputStream getCustomProjectReader() {
+        return getClass().getResourceAsStream('customProject.xml')
+    }
+
+    private Path path(String url) {
+        pathFactory.path(url)
+    }
+}
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/internal/ModuleDependencyBuilderTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/internal/ModuleDependencyBuilderTest.groovy
new file mode 100644
index 0000000..d2c572f
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/internal/ModuleDependencyBuilderTest.groovy
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.idea.model.internal
+
+import spock.lang.Specification
+
+/**
+ * @author Szczepan Faber, @date: 19.03.11
+ */
+class ModuleDependencyBuilderTest extends Specification {
+
+    def builder = new ModuleDependencyBuilder()
+
+    static class ProjectStub {
+        String name
+        IdeaModuleStub ideaModule
+    }
+
+    static class IdeaModuleStub { String moduleName }
+
+    def "builds dependency for project"() {
+        when:
+        def dependency = builder.create(new ProjectStub(ideaModule: new IdeaModuleStub(moduleName: 'services')), 'compile')
+        then:
+        dependency.scope == 'compile'
+        dependency.name == 'services'
+    }
+
+    def "builds dependency for nonIdea project"() {
+        when:
+        def dependency = builder.create(new ProjectStub(name: 'api'), 'compile')
+        then:
+        dependency.scope == 'compile'
+        dependency.name == 'api'
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/GeneratorTaskTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/GeneratorTaskTest.groovy
new file mode 100644
index 0000000..941d50a
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/GeneratorTaskTest.groovy
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.internal
+
+import org.gradle.api.Action
+import org.gradle.plugins.ide.api.GeneratorTask
+import org.gradle.plugins.ide.internal.generator.generator.Generator
+import org.gradle.util.HelperUtil
+import org.gradle.util.TemporaryFolder
+import org.junit.Rule
+import spock.lang.Specification
+
+class GeneratorTaskTest extends Specification {
+    @Rule public final TemporaryFolder tmpDir = new TemporaryFolder()
+    final Generator<TestConfigurationObject> generator = Mock()
+    final File inputFile = tmpDir.file('input')
+    final File outputFile = tmpDir.file('output')
+    final GeneratorTask<TestConfigurationObject> task = HelperUtil.createTask(GeneratorTask)
+
+    def setup() {
+        task.inputFile = inputFile
+        task.outputFile = outputFile
+        task.generator = generator
+    }
+
+    def usesOutputFileAsDefaultInputFile() {
+        when:
+        task.inputFile = null
+
+        then:
+        task.inputFile == task.outputFile
+
+        when:
+        task.inputFile = inputFile
+
+        then:
+        task.inputFile == inputFile
+    }
+
+    def mergesConfigurationWhenInputFileExists() {
+        def configObject = new TestConfigurationObject()
+        inputFile.text = 'config'
+
+        when:
+        task.generate()
+
+        then:
+        1 * generator.read(inputFile) >> configObject
+        1 * generator.configure(configObject)
+        1 * generator.write(configObject, outputFile)
+        0 * _._
+    }
+
+    def generatesConfigurationWhenInputFileDoesNotExist() {
+        def configObject = new TestConfigurationObject()
+
+        when:
+        task.generate()
+
+        then:
+        1 * generator.defaultInstance() >> configObject
+        1 * generator.configure(configObject)
+        1 * generator.write(configObject, outputFile)
+        0 * _._
+    }
+
+    def executesActionBeforeConfiguringObject() {
+        def configObject = new TestConfigurationObject()
+        Action<TestConfigurationObject> action = Mock()
+        task.beforeConfigured(action)
+
+        when:
+        task.generate()
+
+        then:
+        1 * generator.defaultInstance() >> configObject
+        1 * action.execute(configObject)
+        1 * generator.configure(configObject)
+    }
+
+    def executesActionAfterConfiguringObject() {
+        def configObject = new TestConfigurationObject()
+        Action<TestConfigurationObject> action = Mock()
+        task.whenConfigured(action)
+
+        when:
+        task.generate()
+
+        then:
+        1 * generator.defaultInstance() >> configObject
+        1 * generator.configure(configObject)
+        1 * action.execute(configObject)
+    }
+}
+
+class TestConfigurationObject {
+
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/IdePluginTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/IdePluginTest.groovy
new file mode 100644
index 0000000..6a1e00f
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/IdePluginTest.groovy
@@ -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.plugins.ide.internal
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.Project
+import org.gradle.api.Task
+import org.gradle.api.tasks.Delete
+import org.gradle.util.HelperUtil
+import spock.lang.Specification
+
+class IdePluginTest extends Specification {
+    final Project project = HelperUtil.createRootProject()
+
+    def addsLifecycleTasks() {
+        when:
+        new TestIdePlugin().apply(project)
+
+        then:
+        Task ideTask = project.tasks['testIde']
+        ideTask instanceof DefaultTask
+        ideTask.group == 'IDE'
+
+        Task cleanTask = project.tasks['cleanTestIde']
+        cleanTask instanceof DefaultTask
+        cleanTask.group == 'IDE'
+    }
+
+    def addsWorkerTask() {
+        when:
+        new TestIdePlugin().apply(project)
+
+        then:
+        Task worker = project.tasks['generateXml']
+        Task ideTask = project.tasks['testIde']
+        ideTask.taskDependencies.getDependencies(ideTask) == [worker] as Set
+
+        Task cleaner = project.tasks['cleanGenerateXml']
+        cleaner instanceof Delete
+    }
+}
+
+class TestIdePlugin extends IdePlugin {
+    @Override protected String getLifecycleTaskName() {
+        return 'testIde'
+    }
+
+    @Override protected void onApply(Project target) {
+        def worker = target.task('generateXml')
+        addWorker(worker)
+    }
+}
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/configurer/DeduplicationTargetTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/configurer/DeduplicationTargetTest.groovy
new file mode 100644
index 0000000..4533691
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/configurer/DeduplicationTargetTest.groovy
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.internal.configurer
+
+import org.gradle.util.HelperUtil
+import spock.lang.Specification
+
+/**
+ * @author Szczepan Faber, @date: 14.03.11
+ */
+class DeduplicationTargetTest extends Specification {
+
+    def "knows candidate names"() {
+        when:
+        def project = HelperUtil.createRootProject()
+        assert project.name == 'test'
+        def childProject = HelperUtil.createChildProject(project, "child", new File("."))
+        def grandChildProject = HelperUtil.createChildProject(childProject, "grandChild", new File("."))
+
+        then:
+        new DeduplicationTarget(project: project, moduleName: 'test' ).candidateNames == ['test']
+        new DeduplicationTarget(project: childProject, moduleName: 'child' ).candidateNames == ['child', 'test-child']
+        new DeduplicationTarget(project: grandChildProject, moduleName: 'grandChild' ).candidateNames == ['grandChild', 'child-grandChild', 'test-child-grandChild']
+    }
+
+    def "uses passed module name instead of project name"() {
+        when:
+        def project = HelperUtil.createRootProject()
+        assert project.name == 'test'
+        def childProject = HelperUtil.createChildProject(project, "child", new File("."))
+
+        then:
+        new DeduplicationTarget(project: project, moduleName: 'ROOT' ).candidateNames == ['ROOT']
+        new DeduplicationTarget(project: childProject, moduleName: 'CHILD' ).candidateNames == ['CHILD', 'test-CHILD']
+    }
+}
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/configurer/ModuleNameDeduperTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/configurer/ModuleNameDeduperTest.groovy
new file mode 100644
index 0000000..0be9b55
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/configurer/ModuleNameDeduperTest.groovy
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.internal.configurer
+
+import spock.lang.Specification
+
+/**
+ * @author Szczepan Faber, @date: 14.03.11
+ */
+class ModuleNameDeduperTest extends Specification {
+
+    public static class TargetStub extends DeduplicationTarget {
+        String moduleName
+        Collection<String> candidateNames
+        Closure updateModuleName = { moduleName = it }
+    }
+
+    ModuleNameDeduper deduper = new ModuleNameDeduper()
+
+    TargetStub ferrari = new TargetStub(moduleName: "ferrari", candidateNames: [])
+    TargetStub fiat = new TargetStub(moduleName: "fiat", candidateNames: [])
+
+    TargetStub fiatTwo = new TargetStub(moduleName: "fiat", candidateNames: ["parentOne-fiat"])
+    TargetStub fiatThree = new TargetStub(moduleName: "fiat", candidateNames: ["parentTwo-fiat"])
+
+    TargetStub fiatFour = new TargetStub(moduleName: "fiat", candidateNames: ["parentTwo-fiat", "parentThree-parentTwo-fiat"])
+
+    def "does nothing when no duplicates"() {
+        given:
+        def dedupeMe = [ferrari, fiat]
+
+        when:
+        deduper.dedupe(dedupeMe)
+
+        then:
+        dedupeMe == [ferrari, fiat]
+        ferrari.moduleName == "ferrari"
+        fiat.moduleName == "fiat"
+    }
+
+    def "should dedupe module names"() {
+        given:
+        def dedupeMe = [ferrari, fiat, fiatTwo]
+        assert fiatTwo.moduleName == "fiat"
+        assert fiat.moduleName == "fiat"
+
+        when:
+        deduper.dedupe(dedupeMe)
+
+        then:
+        ferrari.moduleName == "ferrari"
+        fiat.moduleName == "fiat"
+        fiatTwo.moduleName == "parentOne-fiat"
+    }
+
+    def "should dedupe module names even if first candidates are dupes"() {
+        given:
+        def dedupeMe = [ferrari, fiat, fiatTwo, fiatThree, fiatFour]
+
+        when:
+        deduper.dedupe(dedupeMe)
+
+        then:
+        ferrari.moduleName == "ferrari"
+        fiat.moduleName == "fiat"
+        fiatTwo.moduleName == "parentOne-fiat"
+        fiatThree.moduleName == "parentTwo-fiat"
+        fiatFour.moduleName == "parentThree-parentTwo-fiat"
+    }
+
+    def "should deal with exactly the same module names"() {
+        given:
+        def module = new TargetStub(moduleName: 'services', candidateNames: [])
+        //module with the same name is theoretically possible if the user overrides the module name
+        def module2 = new TargetStub(moduleName: 'services', candidateNames: ['root-services'])
+        def module3 = new TargetStub(moduleName: 'services', candidateNames: ['root-services'])
+        def module4 = new TargetStub(moduleName: 'services', candidateNames: ['root-services'])
+
+        when:
+        deduper.dedupe([module, module2, module3, module4])
+
+        then:
+        module.moduleName == "services"
+        //if the user hardcoded module name to awkward values we are not deduping it...
+        module2.moduleName == "root-services"
+        module3.moduleName == "services"
+        module4.moduleName == "services"
+    }
+}
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/configurer/ProjectDeduperTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/configurer/ProjectDeduperTest.groovy
new file mode 100644
index 0000000..ee425d9
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/configurer/ProjectDeduperTest.groovy
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ide.internal.configurer
+
+import org.gradle.util.HelperUtil
+import spock.lang.Specification
+
+/**
+ * @author Szczepan Faber, @date: 14.03.11
+ */
+class ProjectDeduperTest extends Specification {
+
+    def project = HelperUtil.createRootProject()
+    def childProject = HelperUtil.createChildProject(project, "child", new File("."))
+    def grandChildProject = HelperUtil.createChildProject(childProject, "grandChild", new File("."))
+
+    def deduper = new ProjectDeduper()
+
+    def "should order projects by depth and pass them to module deduper"() {
+        given:
+        deduper.moduleNameDeduper = Mock(ModuleNameDeduper)
+        def unordered = [childProject, grandChildProject, project]
+
+        when:
+        deduper.dedupe(unordered, { return it })
+
+        then:
+        1 * deduper.moduleNameDeduper.dedupe([project, childProject, grandChildProject])
+    }
+}
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/generator/PropertiesPersistableConfigurationObjectTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/generator/PropertiesPersistableConfigurationObjectTest.groovy
new file mode 100644
index 0000000..8a03430
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/generator/PropertiesPersistableConfigurationObjectTest.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.ide.internal.generator
+
+import org.gradle.util.Matchers
+import org.gradle.util.TemporaryFolder
+import org.junit.Rule
+import spock.lang.Specification
+
+class PropertiesPersistableConfigurationObjectTest extends Specification {
+    @Rule public final TemporaryFolder tmpDir = new TemporaryFolder()
+    String propertyValue
+    final org.gradle.plugins.ide.internal.generator.PropertiesPersistableConfigurationObject object = new org.gradle.plugins.ide.internal.generator.PropertiesPersistableConfigurationObject() {
+        @Override protected String getDefaultResourceName() {
+            return 'defaultResource.properties'
+        }
+
+        @Override protected void load(Properties properties) {
+            propertyValue = properties['prop']
+        }
+
+        @Override protected void store(Properties properties) {
+            properties['prop'] = propertyValue
+        }
+    }
+
+    def loadsFromPropertiesFile() {
+        def inputFile = tmpDir.file('input.properties')
+        inputFile.text = 'prop=value'
+
+        when:
+        object.load(inputFile)
+
+        then:
+        propertyValue == 'value'
+    }
+
+    def loadsFromDefaultResource() {
+        when:
+        object.loadDefaults()
+
+        then:
+        propertyValue == 'default-value'
+    }
+
+    def storesToXmlFile() {
+        object.loadDefaults()
+        propertyValue = 'modified-value'
+        def outputFile = tmpDir.file('output.properties')
+
+        when:
+        object.store(outputFile)
+
+        then:
+        Matchers.containsLine(outputFile.text, 'prop=modified-value')
+    }
+}
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/generator/XmlPersistableConfigurationObjectTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/generator/XmlPersistableConfigurationObjectTest.groovy
new file mode 100644
index 0000000..610f2ab
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/generator/XmlPersistableConfigurationObjectTest.groovy
@@ -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.plugins.ide.internal.generator
+
+import org.gradle.api.internal.XmlTransformer
+import org.gradle.util.TemporaryFolder
+import org.gradle.util.TextUtil
+import org.junit.Rule
+import spock.lang.Specification
+
+class XmlPersistableConfigurationObjectTest extends Specification {
+    @Rule public final TemporaryFolder tmpDir = new TemporaryFolder()
+    String rootElement
+    final XmlPersistableConfigurationObject object = new XmlPersistableConfigurationObject(new XmlTransformer()) {
+        @Override protected String getDefaultResourceName() {
+            return 'defaultResource.xml'
+        }
+
+        @Override protected void load(Node xml) {
+            rootElement = xml.name() as String
+        }
+
+        @Override protected void store(Node xml) {
+            xml.name = rootElement
+        }
+    }
+
+    def loadsFromXmlFile() {
+        def inputFile = tmpDir.file('input.xml')
+        inputFile.text = '<some-xml/>'
+
+        when:
+        object.load(inputFile)
+
+        then:
+        rootElement == 'some-xml'
+    }
+
+    def loadsFromDefaultResource() {
+        when:
+        object.loadDefaults()
+
+        then:
+        rootElement == 'default-xml'
+    }
+
+    def storesToXmlFile() {
+        object.loadDefaults()
+        rootElement = 'modified-xml'
+        def outputFile = tmpDir.file('output.xml')
+
+        when:
+        object.store(outputFile)
+
+        then:
+        outputFile.text == TextUtil.toPlatformLineSeparators('<?xml version="1.0" encoding="UTF-8"?>\n<modified-xml/>\n')
+    }
+}
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/generator/generator/PersistableConfigurationObjectGeneratorTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/generator/generator/PersistableConfigurationObjectGeneratorTest.groovy
new file mode 100644
index 0000000..f422d6d
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/generator/generator/PersistableConfigurationObjectGeneratorTest.groovy
@@ -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.plugins.ide.internal.generator.generator
+
+import spock.lang.Specification
+
+class PersistableConfigurationObjectGeneratorTest extends Specification {
+    final PersistableConfigurationObject object = Mock()
+    final PersistableConfigurationObjectGenerator<PersistableConfigurationObject> generator = new PersistableConfigurationObjectGenerator<PersistableConfigurationObject>() {
+        PersistableConfigurationObject create() {
+            return object
+        }
+        void configure(PersistableConfigurationObject object) {
+        }
+    }
+
+    def readsObjectFromFile() {
+        File inputFile = new File('input')
+
+        when:
+        def result = generator.read(inputFile)
+
+        then:
+        result == object
+        1 * object.load(inputFile)
+        0 * _._
+    }
+
+    def createsDefaultObject() {
+        when:
+        def result = generator.defaultInstance()
+
+        then:
+        result == object
+        1 * object.loadDefaults()
+        0 * _._
+    }
+
+    def writesObjectToFile() {
+        File outputFile = new File('output')
+
+        when:
+        generator.write(object, outputFile)
+
+        then:
+        1 * object.store(outputFile)
+        0 * _._
+    }
+}
diff --git a/subprojects/ide/src/test/groovy/org/gradle/tooling/internal/provider/TasksFactoryTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/tooling/internal/provider/TasksFactoryTest.groovy
new file mode 100644
index 0000000..77924c1
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/tooling/internal/provider/TasksFactoryTest.groovy
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.provider
+
+import org.gradle.api.Project
+import org.gradle.api.Task
+import org.gradle.api.tasks.TaskContainer
+import spock.lang.Specification
+
+class TasksFactoryTest extends Specification {
+    final Project project = Mock()
+    final org.gradle.tooling.internal.protocol.eclipse.EclipseProjectVersion3 eclipseProject = Mock()
+    final TaskContainer tasks = Mock()
+    final TasksFactory factory = new TasksFactory()
+
+    def "builds the tasks for a project"() {
+        def taskA = task('a')
+        def taskB = task('b')
+
+        when:
+        def result = factory.create(project, eclipseProject)
+
+        then:
+        result.size() == 2
+        result[0].path == ':a'
+        result[0].name == 'a'
+        result[0].description == 'task a'
+        result[0].project == eclipseProject
+        result[1].name == 'b'
+        1 * project.tasks >> tasks
+        tasks.iterator() >> [taskA, taskB].iterator()
+    }
+
+    def task(String name) {
+        Task task = Mock()
+        _ * task.path >> ":$name"
+        _ * task.name >> name
+        _ * task.description >> "task $name"
+        return task
+    }
+}
diff --git a/subprojects/ide/src/test/groovy/org/gradle/tooling/internal/provider/dependencies/EclipseProjectDependenciesFactoryTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/tooling/internal/provider/dependencies/EclipseProjectDependenciesFactoryTest.groovy
new file mode 100644
index 0000000..78562a1
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/tooling/internal/provider/dependencies/EclipseProjectDependenciesFactoryTest.groovy
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider.dependencies
+
+import org.gradle.plugins.ide.eclipse.model.ProjectDependency
+import org.gradle.plugins.ide.eclipse.model.SourceFolder
+import org.gradle.tooling.internal.protocol.eclipse.EclipseProjectVersion3
+import spock.lang.Specification
+
+/**
+ * @author Szczepan Faber, @date: 20.03.11
+ */
+class EclipseProjectDependenciesFactoryTest extends Specification {
+
+    def factory = new EclipseProjectDependenciesFactory()
+
+    def "creates instances"() {
+        given:
+        def projectAInstance = Mock(EclipseProjectVersion3)
+        def projectMapping = [':projectA' : projectAInstance]
+        def entries = [
+                new SourceFolder('foo', '', [] as Set, '', [], []),
+                new ProjectDependency('/projectA', true, '', [] as Set, ':projectA'),
+                new ProjectDependency('/projectB', true, '', [] as Set, ':projectB') ]
+
+        when:
+        def deps = factory.create(projectMapping, entries)
+
+        then:
+        deps.size() == 2
+        deps[0].path == 'projectA'
+        deps[0].targetProject == projectAInstance
+        deps[1].path == 'projectB'
+        deps[1].targetProject == null
+    }
+}
diff --git a/subprojects/ide/src/test/groovy/org/gradle/tooling/internal/provider/dependencies/ExternalDependenciesFactoryTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/tooling/internal/provider/dependencies/ExternalDependenciesFactoryTest.groovy
new file mode 100644
index 0000000..1beb3dc
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/tooling/internal/provider/dependencies/ExternalDependenciesFactoryTest.groovy
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider.dependencies
+
+import org.gradle.api.Project
+import org.gradle.plugins.ide.eclipse.model.Library
+import org.gradle.plugins.ide.eclipse.model.SourceFolder
+import spock.lang.Specification
+
+/**
+ * @author Szczepan Faber, @date: 20.03.11
+ */
+class ExternalDependenciesFactoryTest extends Specification {
+
+    def factory = new ExternalDependenciesFactory()
+
+    def "creates instances"() {
+        given:
+        def project = Mock(Project)
+        def somePathDir = new File('/projects/someLibrary')
+        project.file('someLibrary') >> { somePathDir }
+        def entries = [
+                new SourceFolder('foo', '', [] as Set, '', [], []),
+                new Library('someLibrary', true, '', [] as Set, '', '') ]
+
+        when:
+        def deps = factory.create(project, entries)
+
+        then:
+        deps.size() == 1
+        deps[0].file == somePathDir
+    }
+}
diff --git a/subprojects/ide/src/test/groovy/org/gradle/tooling/internal/provider/dependencies/SourceDirectoriesFactoryTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/tooling/internal/provider/dependencies/SourceDirectoriesFactoryTest.groovy
new file mode 100644
index 0000000..66dd9da
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/tooling/internal/provider/dependencies/SourceDirectoriesFactoryTest.groovy
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider.dependencies
+
+import org.gradle.api.Project
+import org.gradle.plugins.ide.eclipse.model.Container
+import org.gradle.plugins.ide.eclipse.model.SourceFolder
+import spock.lang.Specification
+
+/**
+ * @author Szczepan Faber, @date: 20.03.11
+ */
+class SourceDirectoriesFactoryTest extends Specification {
+
+    def factory = new SourceDirectoriesFactory()
+
+    def "creates instances"() {
+        given:
+        def project = Mock(Project)
+        def somePathDir = new File('/projects/somePath')
+        project.file('somePath') >> { somePathDir }
+        def entries = [
+                new SourceFolder('somePath', '', [] as Set, '', [], []),
+                new Container('foo', true, '', [] as Set) ]
+
+        when:
+        def dirs = factory.create(project, entries)
+
+        then:
+        dirs.size() == 1
+        dirs[0].path == 'somePath'
+        dirs[0].directory == somePathDir
+    }
+}
diff --git a/subprojects/eclipse/src/test/resources/org/gradle/plugins/eclipse/model/customClasspath.xml b/subprojects/ide/src/test/resources/org/gradle/plugins/ide/eclipse/model/customClasspath.xml
similarity index 100%
rename from subprojects/eclipse/src/test/resources/org/gradle/plugins/eclipse/model/customClasspath.xml
rename to subprojects/ide/src/test/resources/org/gradle/plugins/ide/eclipse/model/customClasspath.xml
diff --git a/subprojects/eclipse/src/test/resources/org/gradle/plugins/eclipse/model/customOrgEclipseWstCommonComponent.xml b/subprojects/ide/src/test/resources/org/gradle/plugins/ide/eclipse/model/customOrgEclipseWstCommonComponent.xml
similarity index 100%
rename from subprojects/eclipse/src/test/resources/org/gradle/plugins/eclipse/model/customOrgEclipseWstCommonComponent.xml
rename to subprojects/ide/src/test/resources/org/gradle/plugins/ide/eclipse/model/customOrgEclipseWstCommonComponent.xml
diff --git a/subprojects/eclipse/src/test/resources/org/gradle/plugins/eclipse/model/customOrgEclipseWstCommonProjectFacetCoreXml.xml b/subprojects/ide/src/test/resources/org/gradle/plugins/ide/eclipse/model/customOrgEclipseWstCommonProjectFacetCoreXml.xml
similarity index 100%
rename from subprojects/eclipse/src/test/resources/org/gradle/plugins/eclipse/model/customOrgEclipseWstCommonProjectFacetCoreXml.xml
rename to subprojects/ide/src/test/resources/org/gradle/plugins/ide/eclipse/model/customOrgEclipseWstCommonProjectFacetCoreXml.xml
diff --git a/subprojects/ide/src/test/resources/org/gradle/plugins/ide/eclipse/model/customProject.xml b/subprojects/ide/src/test/resources/org/gradle/plugins/ide/eclipse/model/customProject.xml
new file mode 100644
index 0000000..70ae074
--- /dev/null
+++ b/subprojects/ide/src/test/resources/org/gradle/plugins/ide/eclipse/model/customProject.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+    <name>test</name>
+    <comment>for testing</comment> 
+    <projects>
+        <project>refProject</project>
+    </projects>
+    <buildSpec>
+        <buildCommand>
+            <name>org.eclipse.jdt.core.scalabuilder</name>
+            <arguments>
+                <dictionary>
+                    <key>climate</key>
+                    <value>cold</value>
+                </dictionary>
+            </arguments>
+        </buildCommand>
+    </buildSpec>
+    <natures>
+        <nature>org.eclipse.jdt.core.scalanature</nature>
+    </natures>
+    <linkedResources>
+        <link>
+            <name>somename</name>
+            <type>sometype</type>
+            <location>somelocation</location>
+        </link>
+    </linkedResources>
+</projectDescription>
\ No newline at end of file
diff --git a/subprojects/idea/src/test/resources/org/gradle/plugins/idea/model/customModule.xml b/subprojects/ide/src/test/resources/org/gradle/plugins/ide/idea/model/customModule.xml
similarity index 100%
rename from subprojects/idea/src/test/resources/org/gradle/plugins/idea/model/customModule.xml
rename to subprojects/ide/src/test/resources/org/gradle/plugins/ide/idea/model/customModule.xml
diff --git a/subprojects/idea/src/test/resources/org/gradle/plugins/idea/model/customProject.xml b/subprojects/ide/src/test/resources/org/gradle/plugins/ide/idea/model/customProject.xml
similarity index 100%
rename from subprojects/idea/src/test/resources/org/gradle/plugins/idea/model/customProject.xml
rename to subprojects/ide/src/test/resources/org/gradle/plugins/ide/idea/model/customProject.xml
diff --git a/subprojects/idea/src/test/resources/org/gradle/plugins/idea/model/customWorkspace.xml b/subprojects/ide/src/test/resources/org/gradle/plugins/ide/idea/model/customWorkspace.xml
similarity index 100%
rename from subprojects/idea/src/test/resources/org/gradle/plugins/idea/model/customWorkspace.xml
rename to subprojects/ide/src/test/resources/org/gradle/plugins/ide/idea/model/customWorkspace.xml
diff --git a/subprojects/core/src/test/resources/org/gradle/api/internal/tasks/generator/defaultResource.properties b/subprojects/ide/src/test/resources/org/gradle/plugins/ide/internal/generator/defaultResource.properties
similarity index 100%
rename from subprojects/core/src/test/resources/org/gradle/api/internal/tasks/generator/defaultResource.properties
rename to subprojects/ide/src/test/resources/org/gradle/plugins/ide/internal/generator/defaultResource.properties
diff --git a/subprojects/ide/src/test/resources/org/gradle/plugins/ide/internal/generator/defaultResource.xml b/subprojects/ide/src/test/resources/org/gradle/plugins/ide/internal/generator/defaultResource.xml
new file mode 100644
index 0000000..998bf03
--- /dev/null
+++ b/subprojects/ide/src/test/resources/org/gradle/plugins/ide/internal/generator/defaultResource.xml
@@ -0,0 +1 @@
+<default-xml/>
diff --git a/subprojects/idea/idea.gradle b/subprojects/idea/idea.gradle
deleted file mode 100644
index b70403c..0000000
--- a/subprojects/idea/idea.gradle
+++ /dev/null
@@ -1,26 +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.
- */
-dependencies {
-    groovy libraries.groovy_depends
-
-    compile project(':core')
-    compile project(':plugins')
-    compile libraries.slf4j_api
-
-    testCompile project(path: ':core', configuration: 'testFixtures')
-    testRuntime project(path: ':core', configuration: 'testFixturesRuntime')
-}
-
diff --git a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/IdeaModule.groovy b/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/IdeaModule.groovy
deleted file mode 100644
index 7c6cb3c..0000000
--- a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/IdeaModule.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.plugins.idea
-
-import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
-import org.gradle.api.specs.Specs
-import org.gradle.plugins.idea.model.ModuleLibrary
-import org.gradle.plugins.idea.model.Path
-import org.gradle.plugins.idea.model.PathFactory
-import org.gradle.api.tasks.*
-import org.gradle.plugins.idea.model.Module
-import org.gradle.api.artifacts.ProjectDependency
-import org.gradle.api.artifacts.ExternalDependency
-import org.gradle.api.artifacts.ResolvedConfiguration
-import org.gradle.api.artifacts.SelfResolvingDependency
-import org.gradle.api.artifacts.ResolvedDependency
-import org.gradle.api.artifacts.Dependency
-import org.gradle.api.artifacts.Configuration
-
-/**
- * Generates an IDEA module file.
- *
- * @author Hans Dockter
- */
-public class IdeaModule extends XmlGeneratorTask<Module> {
-    /**
-     * The content root directory of the module.
-     */
-    @InputFiles
-    File moduleDir
-
-    /**
-     * The dirs containing the production sources.
-     */
-    @InputFiles
-    Set<File> sourceDirs
-
-    /**
-     * The dirs containing the test sources.
-     */
-    @InputFiles
-    Set<File> testSourceDirs
-
-    /**
-     * The dirs to be excluded by idea.
-     */
-    @InputFiles
-    Set<File> excludeDirs
-
-    /**
-     * The idea output dir for the production sources. If {@code null} no entry for output dirs is created.
-     */
-    @InputFiles @Optional
-    File outputDir
-
-    /**
-     * The idea output dir for the test sources. If {@code null} no entry for test output dirs is created.
-     */
-    @InputFiles @Optional
-    File testOutputDir
-
-    /**
-     * The JDK to use for this module. If this is {@code null} the value of the existing or default ipr XML (inherited)
-     * is used. If it is set to <code>inherited</code>, the project SDK is used. Otherwise the SDK for the corresponding
-     * value of java version is used for this module
-     */
-    @Input @Optional
-    String javaVersion = org.gradle.plugins.idea.model.Module.INHERITED
-
-    /**
-     * Whether to download and add sources associated with the dependency jars.
-     */
-    @Input
-    boolean downloadSources = true
-
-    /**
-     * Whether to download and add javadoc associated with the dependency jars.
-     */
-    @Input
-    boolean downloadJavadoc = false
-
-    /**
-     * The variables to be used for replacing absolute paths in the iml entries. For example, you might add a
-     * {@code GRADLE_USER_HOME} variable to point to the Gradle user home dir.
-     */
-    Map<String, File> variables = [:]
-
-    /**
-     * 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
-     * plus configurations are added minus the files from the minus configurations.
-     */
-    Map<String, Map<String, Configuration>> scopes = [:]
-
-    @Override protected Module create() {
-        return new Module(xmlTransformer, pathFactory)
-    }
-
-    @Override protected void configure(Module module) {
-        module.configure(getContentPath(), getSourcePaths(), getTestSourcePaths(), getExcludePaths(), getOutputPath(), getTestOutputPath(),
-                getDependencies(), javaVersion)
-    }
-
-    protected Path getContentPath() {
-        getPath(project.projectDir)
-    }
-
-    protected Path getOutputPath() {
-        getOutputDir() ? getPath(getOutputDir()) : null
-    }
-
-    protected Path getTestOutputPath() {
-        getTestOutputDir() ? getPath(getTestOutputDir()) : null
-    }
-
-    protected Set getSourcePaths() {
-        getSourceDirs().collect { getPath(it) }
-    }
-
-    protected Set getTestSourcePaths() {
-        getTestSourceDirs().collect { getPath(it) }
-    }
-
-    protected Set getExcludePaths() {
-
-        getExcludeDirs().collect { getPath(it) }
-    }
-
-    protected Set getDependencies() {
-        scopes.keySet().inject([] as LinkedHashSet) {result, scope ->
-            result + (getModuleLibraries(scope) + getModules(scope))
-        }
-    }
-
-    protected Set getModules(String scope) {
-        if (scopes[scope]) {
-            return getScopeDependencies(scopes[scope], { it instanceof ProjectDependency}).collect { ProjectDependency projectDependency ->
-                projectDependency.dependencyProject
-            }.collect { project ->
-                new org.gradle.plugins.idea.model.ModuleDependency(project.name, scope)
-            }
-        }
-        return []
-    }
-
-    protected Set getModuleLibraries(String scope) {
-        if (scopes[scope]) {
-            Set firstLevelDependencies = getScopeDependencies(scopes[scope], { it instanceof ExternalDependency})
-
-            ResolvedConfiguration resolvedConfiguration = project.configurations.detachedConfiguration((firstLevelDependencies as Dependency[])).resolvedConfiguration
-            def allResolvedDependencies = getAllDeps(resolvedConfiguration.firstLevelModuleDependencies)
-
-            Set sourceDependencies = getResolvableDependenciesForAllResolvedDependencies(allResolvedDependencies) { dependency ->
-                addSourceArtifact(dependency)
-            }
-            Map sourceFiles = downloadSources ? getFiles(sourceDependencies, "sources") : [:]
-
-            Set javadocDependencies = getResolvableDependenciesForAllResolvedDependencies(allResolvedDependencies) { dependency ->
-                addJavadocArtifact(dependency)
-            }
-            Map javadocFiles = downloadJavadoc ? getFiles(javadocDependencies, "javadoc") : [:]
-
-            List moduleLibraries = resolvedConfiguration.getFiles(Specs.SATISFIES_ALL).collect { File binaryFile ->
-                File sourceFile = sourceFiles[binaryFile.name]
-                File javadocFile = javadocFiles[binaryFile.name]
-                new ModuleLibrary([getPath(binaryFile)] as Set, javadocFile ? [getPath(javadocFile)] as Set : [] as Set, sourceFile ? [getPath(sourceFile)] as Set : [] as Set, [] as Set, scope)
-            }
-            moduleLibraries.addAll(getSelfResolvingFiles(getScopeDependencies(scopes[scope],
-                    { it instanceof SelfResolvingDependency && !(it instanceof ProjectDependency)}), scope))
-            return moduleLibraries as LinkedHashSet
-        }
-        return []
-    }
-
-    private def getSelfResolvingFiles(Collection dependencies, String scope) {
-        dependencies.inject([] as LinkedHashSet) { result, SelfResolvingDependency selfResolvingDependency ->
-            result.addAll(selfResolvingDependency.resolve().collect { File file ->
-                new ModuleLibrary([getPath(file)] as Set, [] as Set, [] as Set, [] as Set, scope)
-            })
-            result
-        }
-    }
-
-    private Set getScopeDependencies(Map<String, Configuration> configurations, Closure filter) {
-        Set firstLevelDependencies = new LinkedHashSet()
-        configurations.plus.each { Configuration configuration ->
-            firstLevelDependencies.addAll(configuration.getAllDependencies().findAll(filter))
-        }
-        configurations.minus.each { Configuration configuration ->
-            configuration.getAllDependencies().findAll(filter).each { minusDep ->
-                // This deals with dependencies that are defined in different scopes with different
-                // artifacts. Right now we accept the fact, that in such a situation some artifacts
-                // might be duplicated in Idea (they live in different scopes then). 
-                if (minusDep instanceof ExternalDependency) {
-                    ExternalDependency removeCandidate = firstLevelDependencies.find { it == minusDep }
-                    if (removeCandidate && removeCandidate.artifacts == minusDep.artifacts) {
-                        firstLevelDependencies.remove(removeCandidate)
-                    }
-                } else {
-                    firstLevelDependencies.remove(minusDep)
-                }
-            }
-        }
-        return firstLevelDependencies
-    }
-
-    private def getFiles(Set dependencies, String classifier) {
-        return project.configurations.detachedConfiguration((dependencies as Dependency[])).files.inject([:]) { result, sourceFile ->
-            String key = sourceFile.name.replace("-${classifier}.jar", '.jar')
-            result[key] = sourceFile
-            result
-        }
-    }
-
-    private List getResolvableDependenciesForAllResolvedDependencies(Set allResolvedDependencies, Closure configureClosure) {
-        return allResolvedDependencies.collect { ResolvedDependency resolvedDependency ->
-            def dependency = new DefaultExternalModuleDependency(resolvedDependency.moduleGroup, resolvedDependency.moduleName, resolvedDependency.moduleVersion,
-                    resolvedDependency.configuration)
-            dependency.transitive = false
-            configureClosure.call(dependency)
-            dependency
-        }
-    }
-
-    protected Set getAllDeps(Set deps, Set allDeps = []) {
-        deps.each { ResolvedDependency resolvedDependency ->
-            def notSeenBefore = allDeps.add(resolvedDependency)
-            if (notSeenBefore) { // defend against circular dependencies
-                getAllDeps(resolvedDependency.children, allDeps)
-            }
-        }
-        allDeps
-    }
-
-    protected def addSourceArtifact(DefaultExternalModuleDependency dependency) {
-        dependency.artifact { artifact ->
-            artifact.name = dependency.name
-            artifact.type = 'source'
-            artifact.extension = 'jar'
-            artifact.classifier = 'sources'
-        }
-    }
-
-    protected def addJavadocArtifact(DefaultExternalModuleDependency dependency) {
-        dependency.artifact { artifact ->
-            artifact.name = dependency.name
-            artifact.type = 'javadoc'
-            artifact.extension = 'jar'
-            artifact.classifier = 'javadoc'
-        }
-    }
-
-    protected Path getPath(File file) {
-        return pathFactory.path(file)
-    }
-
-    protected PathFactory getPathFactory() {
-        PathFactory factory = new PathFactory()
-        factory.addPathVariable('MODULE_DIR', getOutputFile().parentFile)
-        variables.each { key, value ->
-            factory.addPathVariable(key, value)
-        }
-        return factory
-    }
-}
\ No newline at end of file
diff --git a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/IdeaPlugin.groovy b/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/IdeaPlugin.groovy
deleted file mode 100644
index ef37623..0000000
--- a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/IdeaPlugin.groovy
+++ /dev/null
@@ -1,113 +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.plugins.idea;
-
-
-import org.gradle.api.JavaVersion
-import org.gradle.api.Project
-import org.gradle.api.internal.plugins.IdePlugin
-import org.gradle.api.plugins.JavaPlugin
-
-/**
- * @author Hans Dockter
- *
- * When applied to a project, this plugin add one IdeaModule task. If the project is the root project, the plugin
- * adds also an IdeaProject task.
- *
- * If the java plugin is or has been added to a project where this plugin is applied to, the IdeaModule task gets some
- * Java specific configuration.
- */
-class IdeaPlugin extends IdePlugin {
-    @Override protected String getLifecycleTaskName() {
-        return 'idea'
-    }
-
-    @Override protected void onApply(Project project) {
-        lifecycleTask.description = 'Generates IDEA project files (IML, IPR, IWS)'
-        cleanTask.description = 'Cleans IDEA project files (IML, IPR)'
-        configureIdeaWorkspace(project)
-        configureIdeaProject(project)
-        configureIdeaModule(project)
-        configureForJavaPlugin(project)
-    }
-
-    private def configureIdeaWorkspace(Project project) {
-        if (isRoot(project)) {
-            def task = project.task('ideaWorkspace', description: 'Generates an IDEA workspace file (IWS)', type: IdeaWorkspace) {
-                outputFile = new File(project.projectDir, project.name + ".iws")
-            }
-            addWorker(task)
-        }
-    }
-
-    private def configureIdeaModule(Project project) {
-        def task = project.task('ideaModule', description: 'Generates IDEA module files (IML)', type: IdeaModule) {
-            conventionMapping.outputFile = { new File(project.projectDir, project.name + ".iml") }
-            conventionMapping.moduleDir = { project.projectDir }
-            conventionMapping.sourceDirs = { [] as LinkedHashSet }
-            conventionMapping.excludeDirs = { [project.buildDir, project.file('.gradle')] as LinkedHashSet }
-            conventionMapping.testSourceDirs = { [] as LinkedHashSet }
-        }
-        addWorker(task)
-    }
-
-    private def configureIdeaProject(Project project) {
-        if (isRoot(project)) {
-            def task = project.task('ideaProject', description: 'Generates IDEA project file (IPR)', type: IdeaProject) {
-                outputFile = new File(project.projectDir, project.name + ".ipr")
-                subprojects = project.rootProject.allprojects
-                javaVersion = JavaVersion.VERSION_1_6.toString()
-                wildcards = ['!?*.java', '!?*.groovy']
-            }
-            addWorker(task)
-        }
-    }
-
-    private def configureForJavaPlugin(Project project) {
-        project.plugins.withType(JavaPlugin) {
-            configureIdeaProjectForJava(project)
-            configureIdeaModuleForJava(project)
-        }
-    }
-
-    private def configureIdeaProjectForJava(Project project) {
-        if (isRoot(project)) {
-            project.ideaProject {
-                javaVersion = project.sourceCompatibility
-            }
-        }
-    }
-
-    private def configureIdeaModuleForJava(Project project) {
-        project.ideaModule {
-            conventionMapping.sourceDirs = { project.sourceSets.main.allSource.sourceTrees.srcDirs.flatten() as LinkedHashSet }
-            conventionMapping.testSourceDirs = { project.sourceSets.test.allSource.sourceTrees.srcDirs.flatten() as LinkedHashSet }
-            conventionMapping.outputDir = { new File("$project.projectDir/out/production/$project.name") }
-            conventionMapping.testOutputDir = { new File("$project.projectDir/out/test/$project.name") }
-            def configurations = project.configurations
-            scopes = [
-                    COMPILE: [plus: [configurations.compile], minus: []],
-                    RUNTIME: [plus: [configurations.runtime], minus: [configurations.compile]],
-                    TEST: [plus: [configurations.testRuntime], minus: [configurations.runtime]]
-            ]
-        }
-    }
-
-    private boolean isRoot(Project project) {
-        return project.parent == null
-    }
-}
-
diff --git a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/IdeaProject.groovy b/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/IdeaProject.groovy
deleted file mode 100644
index aba3d25..0000000
--- a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/IdeaProject.groovy
+++ /dev/null
@@ -1,68 +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.plugins.idea
-
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.XmlGeneratorTask
-import org.gradle.plugins.idea.model.ModulePath
-import org.gradle.plugins.idea.model.PathFactory
-import org.gradle.plugins.idea.model.Project
-
-/**
- * Generates an IDEA project file.
- *
- * @author Hans Dockter
- */
-public class IdeaProject extends XmlGeneratorTask<Project> {
-    /**
-     * The subprojects that should be mapped to modules in the ipr file. The subprojects will only be mapped if the Idea plugin has been
-     * applied to them.
-     */
-    Set<Project> subprojects
-
-    /**
-     * The java version used for defining the project sdk.
-     */
-    @Input
-    String javaVersion
-
-    /**
-     * The wildcard resource patterns.
-     */
-    @Input
-    Set wildcards
-
-    Project create() {
-        return new Project(xmlTransformer, getPathFactory())
-    }
-
-    void configure(Project ideaProject) {
-        Set modules = subprojects.inject(new LinkedHashSet()) { result, subproject ->
-            if (subproject.plugins.hasPlugin(IdeaPlugin)) {
-                File imlFile = subproject.ideaModule.outputFile
-                result << new ModulePath(pathFactory.relativePath('PROJECT_DIR', imlFile))
-            }
-            result
-        }
-        ideaProject.configure(modules, javaVersion, wildcards)
-    }
-
-    PathFactory getPathFactory() {
-        PathFactory factory = new PathFactory()
-        factory.addPathVariable('PROJECT_DIR', outputFile.parentFile)
-        return factory
-    }
-}
diff --git a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/IdeaWorkspace.groovy b/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/IdeaWorkspace.groovy
deleted file mode 100644
index d863a65..0000000
--- a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/IdeaWorkspace.groovy
+++ /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.plugins.idea
-
-import org.gradle.api.tasks.XmlGeneratorTask
-import org.gradle.plugins.idea.model.Workspace
-
-/**
- * Generates an IDEA workspace file.
- *
- * @author Hans Dockter
- */
-public class IdeaWorkspace extends XmlGeneratorTask<Workspace> {
-    @Override protected Workspace create() {
-        return new Workspace(xmlTransformer)
-    }
-
-    @Override protected void configure(Workspace object) {
-    }
-}
diff --git a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/Dependency.java b/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/Dependency.java
deleted file mode 100644
index 89ef6ef..0000000
--- a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/Dependency.java
+++ /dev/null
@@ -1,27 +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.plugins.idea.model;
-
-import groovy.util.Node;
-
-/**
- * Represents a dependency of an IDEA module.
- *
- * @author Hans Dockter
- */
-public interface Dependency {
-    void addToNode(Node parentNode);
-}
diff --git a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/JarDirectory.groovy b/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/JarDirectory.groovy
deleted file mode 100644
index 6f52f97..0000000
--- a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/JarDirectory.groovy
+++ /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.plugins.idea.model
-
-/**
- * Represents a jar directory element of an idea module library.
- *
- * @author Hans Dockter
- */
-class JarDirectory {
-    /**
-     * The path of the jar directory
-     */
-    Path path
-
-    /**
-     * The value for the recursive attribute of the jar directory element.
-     */
-    boolean recursive
-
-    def JarDirectory(path, recursive) {
-        this.path = path;
-        this.recursive = recursive;
-    }
-
-    boolean equals(o) {
-        if (this.is(o)) { return true }
-
-        if (getClass() != o.class) { return false }
-
-        JarDirectory that = (JarDirectory) o;
-
-        if (recursive != that.recursive) { return false }
-        if (path != that.path) { return false }             
-
-        return true;
-    }
-
-    int hashCode() {
-        int result;
-
-        result = path.hashCode();
-        result = 31 * result + (recursive ? 1 : 0);
-        return result;
-    }
-
-    public String toString() {
-        return "JarDirectory{" +
-                "path=" + path +
-                ", recursive=" + recursive +
-                '}';
-    }
-}
diff --git a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/Jdk.groovy b/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/Jdk.groovy
deleted file mode 100644
index 20de840..0000000
--- a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/Jdk.groovy
+++ /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.plugins.idea.model
-
-/**
- * Represents a the information for the project JavaSDK. This information are attributes of the ProjectRootManager
- * element in the ipr.
- * 
- * @author Hans Dockter
- */
-class Jdk {
-    boolean assertKeyword
-    boolean jdk15 = false
-    String languageLevel
-    String projectJdkName
-
-    def Jdk(String javaVersion) {
-        if (javaVersion.startsWith("1.4")) {
-            assertKeyword = true
-            jdk15 = false
-            languageLevel = 'JDK_1_4'
-        }
-        else if (javaVersion.startsWith("1.5")) {
-            assertKeyword = true
-            jdk15 = true
-            languageLevel = 'JDK_1_5'
-        }
-        else if (javaVersion >= '1.6') {
-            assertKeyword = true
-            jdk15 = true
-            languageLevel = 'JDK_1_6'
-        }
-        else {
-            assertKeyword = false
-        }
-        projectJdkName = javaVersion
-    }
-
-    def Jdk(assertKeyword, jdk15, languageLevel, projectJdkName) {
-        this.assertKeyword = assertKeyword;
-        this.jdk15 = jdk15;
-        this.languageLevel = languageLevel
-        this.projectJdkName = projectJdkName;
-    }
-
-    boolean equals(o) {
-        if (this.is(o)) { return true }
-
-        if (getClass() != o.class) { return false }
-
-        Jdk jdk = (Jdk) o;
-
-        if (assertKeyword != jdk.assertKeyword) { return false }
-        if (jdk15 != jdk.jdk15) { return false }
-        if (languageLevel != jdk.languageLevel) { return false }
-        if (projectJdkName != jdk.projectJdkName) { return false }
-
-        return true;
-    }
-
-    int hashCode() {
-        int result;
-
-        result = 31 * result + (assertKeyword ? 1 : 0);
-        result = 31 * result + (jdk15 ? 1 : 0);
-        result = 31 * result + (languageLevel != null ? languageLevel.hashCode() : 0);
-        result = 31 * result + (projectJdkName != null ? projectJdkName.hashCode() : 0);
-        return result;
-    }
-
-
-    public String toString() {
-        return "Jdk{" +
-                "assertKeyword=" + assertKeyword +
-                ", jdk15=" + jdk15 +
-                ", languageLevel='" + languageLevel +
-                "', projectJdkName='" + projectJdkName + '\'' +
-                '}';
-    }
-}
diff --git a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/Module.groovy b/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/Module.groovy
deleted file mode 100644
index 27fefd9..0000000
--- a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/Module.groovy
+++ /dev/null
@@ -1,317 +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.plugins.idea.model
-
-import org.gradle.api.internal.XmlTransformer
-import org.gradle.api.internal.tasks.generator.XmlPersistableConfigurationObject
-
-/**
- * Represents the customizable elements of an iml (via XML hooks everything of the iml is customizable).
- *
- * @author Hans Dockter
- */
-class Module extends XmlPersistableConfigurationObject {
-    static final String INHERITED = "inherited"
-
-    /**
-     * The dir for the content root of the module.  Defaults to the projectDir for the project.  If null,
-     * the directory that contains the output file will be used.
-     */
-    Path contentPath;
-
-    /**
-     * The foldes for the production code. Must not be null.
-     */
-    Set sourceFolders = [] as LinkedHashSet
-
-    /**
-     * The folders for the test code. Must not be null.
-     */
-    Set testSourceFolders = [] as LinkedHashSet
-
-    /**
-     * Folders to be excluded. Must not be null.
-     */
-    Set excludeFolders = [] as LinkedHashSet
-
-    /**
-     * The dir for the production source classes. If null this output dir element is not added.
-     */
-    Path outputDir
-
-    /**
-     * The dir for the compiled test source classes. If null this output element is not added.
-     */
-    Path testOutputDir
-
-    /**
-     * The dependencies of this module. Must not be null. Has instances of type    {@link Dependency}   .
-     */
-    Set dependencies = [] as LinkedHashSet
-
-    String javaVersion
-
-    private final PathFactory pathFactory
-
-    def Module(XmlTransformer withXmlActions, PathFactory pathFactory) {
-        super(withXmlActions)
-        this.pathFactory = pathFactory
-    }
-
-    @Override protected String getDefaultResourceName() {
-        return 'defaultModule.xml'
-    }
-
-    @Override protected void load(Node xml) {
-        readJdkFromXml()
-        readSourceAndExcludeFolderFromXml()
-        readOutputDirsFromXml()
-        readDependenciesFromXml()
-    }
-
-    private def readJdkFromXml() {
-        def jdk = findOrderEntries().find { it. at type == 'jdk' }
-        if (jdk) {
-            this.javaVersion = jdk. at jdkName
-        } else {
-            this.javaVersion = INHERITED
-        }
-    }
-
-    private def readOutputDirsFromXml() {
-        def outputDirUrl = findOutputDir()?. at url
-        def testOutputDirUrl = findTestOutputDir()?. at url
-        this.outputDir = outputDirUrl ? pathFactory.path(outputDirUrl) : null
-        this.testOutputDir = testOutputDirUrl ? pathFactory.path(testOutputDirUrl) : null
-    }
-
-    private def readDependenciesFromXml() {
-        return findOrderEntries().each { orderEntry ->
-            switch (orderEntry. at type) {
-                case "module-library":
-                    Set classes = orderEntry.library.CLASSES.root.collect {
-                        pathFactory.path(it. at url)
-                    }
-                    Set javadoc = orderEntry.library.JAVADOC.root.collect {
-                        pathFactory.path(it. at url)
-                    }
-                    Set sources = orderEntry.library.SOURCES.root.collect {
-                        pathFactory.path(it. at url)
-                    }
-                    Set jarDirectories = orderEntry.library.jarDirectory.collect { new JarDirectory(pathFactory.path(it. at url), Boolean.parseBoolean(it. at recursive)) }
-                    def moduleLibrary = new ModuleLibrary(classes, javadoc, sources, jarDirectories, orderEntry. at scope)
-                    dependencies.add(moduleLibrary)
-                    break
-                case "module":
-                    dependencies.add(new ModuleDependency(orderEntry.@'module-name', orderEntry. at scope))
-            }
-        }
-    }
-
-    private def readSourceAndExcludeFolderFromXml() {
-        findSourceFolder().each { sourceFolder ->
-            if (sourceFolder. at isTestSource == 'false') {
-                this.sourceFolders.add(pathFactory.path(sourceFolder. at url))
-            } else {
-                this.testSourceFolders.add(pathFactory.path(sourceFolder. at url))
-            }
-        }
-        findExcludeFolder().each { excludeFolder ->
-            this.excludeFolders.add(pathFactory.path(excludeFolder. at url))
-        }
-    }
-
-    def configure(Path contentPath, Set sourceFolders, Set testSourceFolders, Set excludeFolders, Path outputDir, Path testOutputDir, Set dependencies,
-                  String javaVersion) {
-        this.contentPath = contentPath
-        this.sourceFolders.addAll(sourceFolders);
-        this.testSourceFolders.addAll(testSourceFolders);
-        this.excludeFolders.addAll(excludeFolders);
-        if (outputDir) {
-            this.outputDir = outputDir
-        }
-        if (testOutputDir) {
-            this.testOutputDir = testOutputDir;
-        }
-        this.dependencies = dependencies; // overwrite rather than append dependencies
-        if (javaVersion) {
-            this.javaVersion = javaVersion
-        }
-    }
-
-    @Override protected void store(Node xml) {
-        addJdkToXml()
-        setContentURL()
-        removeSourceAndExcludeFolderFromXml()
-        addSourceAndExcludeFolderToXml()
-        addOutputDirsToXml()
-
-        removeDependenciesFromXml()
-        addDependenciesToXml()
-    }
-
-    private def addJdkToXml() {
-        assert javaVersion != null
-        Node moduleJdk = findOrderEntries().find { it. at type == 'jdk' }
-        if (javaVersion != INHERITED) {
-            Node inheritedJdk = findOrderEntries().find { it. at type == "inheritedJdk" }
-            if (inheritedJdk) {
-                inheritedJdk.parent().remove(inheritedJdk)
-            }
-            if (moduleJdk) {
-                findNewModuleRootManager().remove(moduleJdk)
-            }
-            findNewModuleRootManager().appendNode("orderEntry", [type: "jdk", jdkName: javaVersion, jdkType: "JavaSDK"])
-        } else if (!(findOrderEntries().find { it. at type == "inheritedJdk" })) {
-            if (moduleJdk) {
-                findNewModuleRootManager().remove(moduleJdk)
-            }
-            findNewModuleRootManager().appendNode("orderEntry", [type: "inheritedJdk"])
-        }
-    }
-
-    private def setContentURL() {
-        if (contentPath != null) {
-            findContent(). at url = contentPath.url
-        }
-    }
-
-    private def addOutputDirsToXml() {
-        if (outputDir) {
-            findOrCreateOutputDir(). at url = outputDir.url
-        }
-        if (testOutputDir) {
-            findOrCreateTestOutputDir(). at url = testOutputDir.url
-        }
-    }
-
-    private Node findOrCreateOutputDir() {
-        return findOutputDir() ?: findNewModuleRootManager().appendNode("output")
-    }
-
-    private Node findOrCreateTestOutputDir() {
-        return findTestOutputDir() ?: findNewModuleRootManager().appendNode("output-test")
-    }
-
-    private Set addDependenciesToXml() {
-        return dependencies.each { Dependency dependency ->
-            dependency.addToNode(findNewModuleRootManager())
-        }
-    }
-
-    private def addSourceAndExcludeFolderToXml() {
-        sourceFolders.each { Path path ->
-            findContent().appendNode('sourceFolder', [url: path.url, isTestSource: 'false'])
-        }
-        testSourceFolders.each { Path path ->
-            findContent().appendNode('sourceFolder', [url: path.url, isTestSource: 'true'])
-        }
-        excludeFolders.each { Path path ->
-            findContent().appendNode('excludeFolder', [url: path.url])
-        }
-    }
-
-    private def removeSourceAndExcludeFolderFromXml() {
-        findSourceFolder().each { sourceFolder ->
-            findContent().remove(sourceFolder)
-        }
-        findExcludeFolder().each { excludeFolder ->
-            findContent().remove(excludeFolder)
-        }
-    }
-
-    private def removeDependenciesFromXml() {
-        return findOrderEntries().each { orderEntry ->
-            if (isDependencyOrderEntry(orderEntry)) {
-                findNewModuleRootManager().remove(orderEntry)
-            }
-        }
-    }
-
-    protected boolean isDependencyOrderEntry(def orderEntry) {
-        ['module-library', 'module'].contains(orderEntry. at type)
-    }
-
-    private Node findContent() {
-        findNewModuleRootManager().content[0]
-    }
-
-    private def findSourceFolder() {
-        return findContent().sourceFolder
-    }
-
-    private def findExcludeFolder() {
-        return findContent().excludeFolder
-    }
-
-    private Node findOutputDir() {
-        return findNewModuleRootManager().output[0]
-    }
-
-    private Node findNewModuleRootManager() {
-        return xml.component.find { it. at name == 'NewModuleRootManager'}
-    }
-
-    private Node findTestOutputDir() {
-        return findNewModuleRootManager().'output-test'[0]
-    }
-
-    private def findOrderEntries() {
-        return findNewModuleRootManager().orderEntry
-    }
-
-
-    boolean equals(o) {
-        if (this.is(o)) { return true }
-
-        if (getClass() != o.class) { return false }
-
-        Module module = (Module) o;
-
-        if (dependencies != module.dependencies) { return false }
-        if (excludeFolders != module.excludeFolders) { return false }
-        if (outputDir != module.outputDir) { return false }
-        if (sourceFolders != module.sourceFolders) { return false }
-        if (testOutputDir != module.testOutputDir) { return false }
-        if (testSourceFolders != module.testSourceFolders) { return false }
-
-        return true;
-    }
-
-    int hashCode() {
-        int result;
-
-        result = (sourceFolders != null ? sourceFolders.hashCode() : 0);
-        result = 31 * result + (testSourceFolders != null ? testSourceFolders.hashCode() : 0);
-        result = 31 * result + (excludeFolders != null ? excludeFolders.hashCode() : 0);
-        result = 31 * result + outputDir.hashCode();
-        result = 31 * result + testOutputDir.hashCode();
-        result = 31 * result + (dependencies != null ? dependencies.hashCode() : 0);
-        return result;
-    }
-
-
-    public String toString() {
-        return "Module{" +
-                "dependencies=" + dependencies +
-                ", sourceFolders=" + sourceFolders +
-                ", testSourceFolders=" + testSourceFolders +
-                ", excludeFolders=" + excludeFolders +
-                ", outputDir=" + outputDir +
-                ", testOutputDir=" + testOutputDir +
-                '}';
-    }
-}
diff --git a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/ModuleDependency.groovy b/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/ModuleDependency.groovy
deleted file mode 100644
index 32da7d1..0000000
--- a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/ModuleDependency.groovy
+++ /dev/null
@@ -1,91 +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.plugins.idea.model
-
-/**
- * Represents an orderEntry of type module in the iml xml.
- *
- * @author Hans Dockter
- */
-class ModuleDependency implements Dependency {
-    /**
-     * The name of the module the module depends on. Must not be null.
-     */
-    String name
-
-    /**
-     * The scope for this dependency. If null the scope attribute is not added.
-     */
-    String scope
-
-    boolean exported
-
-    def ModuleDependency(name, scope) {
-        this.name = name;
-        this.scope = scope;
-        this.exported = !scope || scope == 'COMPILE' || scope == 'RUNTIME'
-    }
-
-    void addToNode(Node parentNode) {
-        parentNode.appendNode('orderEntry', [type: 'module', 'module-name': name] + getAttributeMapForScopeAndExported())
-    }
-
-    private Map getAttributeMapForScopeAndExported() {
-        return (exported ? [exported: ""] : [:]) + ((scope && scope != 'COMPILE') ? [scope: scope] : [:])
-    }
-
-    boolean equals(o) {
-        if (this.is(o)) { return true }
-
-        if (getClass() != o.class) { return false }
-
-        ModuleDependency that = (ModuleDependency) o;
-
-        if (name != that.name) { return false }
-        if (!scopeEquals(scope, that.scope)) { return false }
-
-        return true;
-    }
-
-    private boolean scopeEquals(String lhs, String rhs) {
-        if (lhs == 'COMPILE') {
-            return !rhs || rhs == 'COMPILE'
-        } else if (rhs == 'COMPILE') {
-            return !lhs || lhs == 'COMPILE'
-        } else {
-            return lhs == rhs
-        }
-    }
-
-    int hashCode() {
-        int result;
-
-        result = name.hashCode();
-        result = 31 * result + getScopeHash();
-        return result;
-    }
-
-    private int getScopeHash() {
-        (scope && scope != 'COMPILE' ? scope.hashCode() : 0)
-    }
-
-    public String toString() {
-        return "ModuleDependency{" +
-                "name='" + name + '\'' +
-                ", scope='" + scope + '\'' +
-                '}';
-    }
-}
diff --git a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/ModuleLibrary.groovy b/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/ModuleLibrary.groovy
deleted file mode 100644
index d6e1b00..0000000
--- a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/ModuleLibrary.groovy
+++ /dev/null
@@ -1,135 +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.plugins.idea.model
-
-/**
- * Represents an orderEntry of type module-library in the iml xml.
- *
- * @author Hans Dockter
- */
-class ModuleLibrary implements Dependency {
-    /**
-     * A set of  {@link org.gradle.plugins.idea.model.Path}  instances for class libraries.
-     */
-    Set classes
-
-    /**
-     * A set of  {@link org.gradle.plugins.idea.model.JarDirectory}  instances for directories containing jars.
-     */
-    Set jarDirectories
-
-    /**
-     * A set of  {@link org.gradle.plugins.idea.model.Path}  instances for javadoc associated with the library elements.
-     */
-    Set javadoc
-
-    /**
-     * A set of  {@link org.gradle.plugins.idea.model.Path}  instances for source code associated with the library elements.
-     */
-    Set sources
-
-    /**
-     * The scope of this dependency. If null the scope attribute is not added.
-     */
-    String scope
-
-    boolean exported
-
-    def ModuleLibrary(classes, javadoc, sources, jarDirectories, scope) {
-        this.classes = classes;
-        this.jarDirectories = jarDirectories;
-        this.javadoc = javadoc;
-        this.sources = sources;
-        this.scope = scope
-        this.exported = !scope || scope == 'COMPILE' || scope == 'RUNTIME'
-    }
-
-    void addToNode(Node parentNode) {
-        Node libraryNode = parentNode.appendNode('orderEntry', [type: 'module-library'] + getAttributeMapForScopeAndExported()).appendNode('library')
-        Node classesNode = libraryNode.appendNode('CLASSES')
-        Node javadocNode = libraryNode.appendNode('JAVADOC')
-        Node sourcesNode = libraryNode.appendNode('SOURCES')
-        classes.each { Path path ->
-            classesNode.appendNode('root', [url: path.url])
-        }
-        javadoc.each { Path path ->
-            javadocNode.appendNode('root', [url: path.url])
-        }
-        sources.each { Path path ->
-            sourcesNode.appendNode('root', [url: path.url])
-        }
-        jarDirectories.each { JarDirectory jarDirectory ->
-            libraryNode.appendNode('jarDirectory', [url: jarDirectory.path.url, recursive: jarDirectory.recursive])
-        }
-    }
-
-    private Map getAttributeMapForScopeAndExported() {
-        return (exported ? [exported: ""] : [:]) + ((scope && scope != 'COMPILE') ? [scope: scope] : [:])
-    }
-
-
-    boolean equals(o) {
-        if (this.is(o)) { return true }
-
-        if (getClass() != o.class) { return false }
-
-        ModuleLibrary that = (ModuleLibrary) o;
-
-        if (classes != that.classes) { return false }
-        if (jarDirectories != that.jarDirectories) { return false }
-        if (javadoc != that.javadoc) { return false }
-        if (!scopeEquals(scope, that.scope)) { return false }
-        if (sources != that.sources) { return false }
-
-        return true;
-    }
-
-    private boolean scopeEquals(String lhs, String rhs) {
-        if (lhs == 'COMPILE') {
-            return !rhs || rhs == 'COMPILE'
-        } else if (rhs == 'COMPILE') {
-            return !lhs || lhs == 'COMPILE'
-        } else {
-            return lhs == rhs
-        }
-    }
-
-
-    int hashCode() {
-        int result;
-
-        result = classes.hashCode();
-        result = 31 * result + jarDirectories.hashCode();
-        result = 31 * result + javadoc.hashCode();
-        result = 31 * result + sources.hashCode();
-        result = 31 * result + getScopeHash()
-        return result;
-    }
-
-    private int getScopeHash() {
-        (scope && scope != 'COMPILE' ? scope.hashCode() : 0)
-    }
-
-    public String toString() {
-        return "ModuleLibrary{" +
-                "classes=" + classes +
-                ", jarDirectories=" + jarDirectories +
-                ", javadoc=" + javadoc +
-                ", sources=" + sources +
-                ", scope='" + scope + '\'' +
-                '}';
-    }
-}
diff --git a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/ModulePath.groovy b/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/ModulePath.groovy
deleted file mode 100644
index ee7678c..0000000
--- a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/ModulePath.groovy
+++ /dev/null
@@ -1,65 +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.plugins.idea.model
-
-/**
- * Represents a path in a format as used often in ipr and iml files.
- *
- * @author Hans Dockter
- */
-
-class ModulePath {
-    /**
-     * The path string of this path.
-     */
-    final String filePath
-
-    final Path path
-
-    def ModulePath(Path path) {
-        this.path = path
-        filePath = path.relPath
-    }
-
-    def ModulePath(Path path, String filePath) {
-        this.path = path
-        this.filePath = filePath
-    }
-
-    String getUrl() {
-        return path.url
-    }
-    
-    boolean equals(o) {
-        if (this.is(o)) { return true }
-
-        if (o == null || getClass() != o.class) { return false }
-
-        ModulePath that = (ModulePath) o;
-        return path == that.path && filePath == that.filePath
-    }
-
-    int hashCode() {
-        return path.hashCode() ^ filePath.hashCode()
-    }
-
-    public String toString() {
-        return "ModulePath{" +
-                "path='" + path +
-                ", filePath='" + filePath + '\'' +
-                '}';
-    }
-}
\ No newline at end of file
diff --git a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/Path.groovy b/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/Path.groovy
deleted file mode 100644
index 436ecb8..0000000
--- a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/Path.groovy
+++ /dev/null
@@ -1,149 +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.plugins.idea.model
-
-/**
- * Represents a path in a format as used often in ipr and iml files.
- *
- * @author Hans Dockter
- */
-class Path {
-    /**
-     * The url of the path. Must not be null
-     */
-    final String url
-
-    /**
-     * The relative path of the path. Must not be null
-     */
-    final String relPath
-
-    final String canonicalUrl
-
-    def Path(File rootDir, String rootDirString, File file) {
-        relPath = getRelativePath(rootDir, rootDirString, file)
-        url = relativePathToURI(relPath)
-        canonicalUrl = relativePathToURI(file.absolutePath.replace(File.separator, '/'))
-    }
-
-    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)
-        canonicalUrl = url
-    }
-
-    def Path(String url) {
-        this(url, url)
-    }
-
-    def Path(String url, String canonicalUrl) {
-        this.relPath = null
-        this.url = url
-        this.canonicalUrl = canonicalUrl
-    }
-
-    private static String getRelativePath(File rootDir, String rootDirString, File file) {
-        String relpath = getRelativePath(rootDir, file)
-        return relpath != null ? rootDirString + '/' + relpath : file.absolutePath.replace(File.separator, '/')
-    }
-
-    private static String relativePathToURI(String relpath) {
-        if (relpath.endsWith('.jar')) {
-            return 'jar://' + relpath + '!/';
-        } else {
-            return 'file://' + relpath;
-        }
-    }
-
-    // This gets a relative path even if neither path is an ancestor of the other.
-    // implementation taken from http://www.devx.com/tips/Tip/13737 and slighly modified
-    //@param relativeTo  the destinationFile
-    //@param fromFile    where the relative path starts
-
-    private static String getRelativePath(File relativeTo, File fromFile) {
-        return matchPathLists(getPathList(relativeTo), getPathList(fromFile))
-    }
-
-    private static List getPathList(File f) {
-        List list = []
-        File r = f.canonicalFile
-        while (r != null) {
-            File parent = r.parentFile
-            list.add(parent ? r.name : r.absolutePath)
-            r = parent
-        }
-
-        return list
-    }
-
-    private static String matchPathLists(List r, List f) {
-        StringBuilder s = new StringBuilder();
-
-        // 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--
-        }
-
-        // for each remaining level in the relativeTo path, add a ..
-        for (; i >= 0; i--) {
-            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])
-        }
-
-        return s.toString()
-    }
-
-    boolean equals(o) {
-        if (this.is(o)) { return true }
-
-        if (o == null || getClass() != o.class) { return false }
-
-        Path path = (Path) o;
-
-        if (canonicalUrl != path.canonicalUrl) { return false }
-
-        return true;
-    }
-
-    int hashCode() {
-        return canonicalUrl.hashCode();
-    }
-
-    public String toString() {
-        return "Path{" +
-                "url='" + url + '\'' +
-                ", canonicalUrl='" + canonicalUrl + '\'' +
-                '}';
-    }
-}
diff --git a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/PathFactory.groovy b/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/PathFactory.groovy
deleted file mode 100644
index 59582f8..0000000
--- a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/PathFactory.groovy
+++ /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.plugins.idea.model
-
-class PathFactory {
-    private final List<Map> variables = []
-    private final Map<String, File> varsByName = [:]
-
-    void addPathVariable(String name, File dir) {
-        variables << [name: "\$${name}\$", prefix: dir.absolutePath + File.separator, dir: dir]
-        varsByName[name] = dir
-    }
-
-    /**
-     * Creates a path for the given file.
-     */
-    Path path(File file) {
-        createFile(file.canonicalFile)
-    }
-
-    private Path createFile(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)
-    }
-
-    /**
-     * Creates a path relative to the given path variable.
-     */
-    Path relativePath(String pathVar, File file) {
-        return new Path(varsByName[pathVar], "\$${pathVar}\$", file)
-    }
-
-    /**
-     * Creates a path for the given URL.
-     */
-    Path path(String url) {
-        String expandedUrl = url
-        for (variable in variables) {
-            expandedUrl = expandedUrl.replace(variable.name, variable.prefix)
-        }
-        if (expandedUrl.toLowerCase().startsWith('file://')) {
-            expandedUrl = toUrl('file', new File(expandedUrl.substring(7)).canonicalFile)
-        } else if (expandedUrl.toLowerCase().startsWith('jar://')) {
-            def parts = expandedUrl.substring(6).split('!')
-            if (parts.length == 2) {
-                expandedUrl = toUrl('jar', new File(parts[0]).canonicalFile) + '!' + parts[1]
-            }
-        }
-        return new Path(url, expandedUrl)
-    }
-
-    def toUrl(String scheme, File file) {
-        return scheme + '://' + file.absolutePath.replace(File.separator, '/')
-    }
-}
diff --git a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/Project.groovy b/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/Project.groovy
deleted file mode 100644
index c215da7..0000000
--- a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/Project.groovy
+++ /dev/null
@@ -1,135 +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.plugins.idea.model
-
-import org.gradle.api.internal.tasks.generator.XmlPersistableConfigurationObject
-import org.gradle.api.internal.XmlTransformer
-
-/**
- * Represents the customizable elements of an ipr (via XML hooks everything of the ipr is customizable).
- *
- * @author Hans Dockter
- */
-class Project extends XmlPersistableConfigurationObject {
-    /**
-     * A set of {@link Path} instances pointing to the modules contained in the ipr.
-     */
-    Set modulePaths = []
-
-    /**
-     * A set of wildcard string to be included/excluded from the resources.
-     */
-    Set wildcards = []
-
-    /**
-     * Represent the jdk information of the project java sdk.
-     */
-    Jdk jdk
-
-    private final PathFactory pathFactory
-
-    def Project(XmlTransformer xmlTransformer, PathFactory pathFactory) {
-        super(xmlTransformer)
-        this.pathFactory = pathFactory
-    }
-
-    def configure(Set modulePaths, String javaVersion, Set wildcards) {
-        if (javaVersion) {
-            jdk = new Jdk(javaVersion)
-        }
-
-        this.modulePaths.addAll(modulePaths)
-        this.wildcards.addAll(wildcards)
-    }
-
-    @Override protected void load(Node xml) {
-        findModules().module.each { module ->
-            this.modulePaths.add(new ModulePath(pathFactory.path(module. at fileurl), module. at filepath))
-        }
-
-        findWildcardResourcePatterns().entry.each { entry ->
-            this.wildcards.add(entry. at name)
-        }
-        def jdkValues = findProjectRootManager().attributes()
-
-        jdk = new Jdk(Boolean.parseBoolean(jdkValues.'assert-keyword'), Boolean.parseBoolean(jdkValues.'jdk-15'),
-                jdkValues.languageLevel, jdkValues.'project-jdk-name')
-    }
-
-    @Override protected String getDefaultResourceName() {
-        return "defaultProject.xml"
-    }
-
-    @Override protected void store(Node xml) {
-        findModules().replaceNode {
-            modules {
-                modulePaths.each { ModulePath modulePath ->
-                    module(fileurl: modulePath.path.url, filepath: modulePath.filePath)
-                }
-            }
-        }
-        findWildcardResourcePatterns().replaceNode {
-            wildcardResourcePatterns {
-                this.wildcards.each { wildcard ->
-                    entry(name: wildcard)
-                }
-            }
-        }
-        findProjectRootManager().@'assert-keyword' = jdk.assertKeyword
-        findProjectRootManager().@'assert-jdk-15' = jdk.jdk15
-        findProjectRootManager(). at languageLevel = jdk.languageLevel
-        findProjectRootManager().@'project-jdk-name' = jdk.projectJdkName
-    }
-
-    private def findProjectRootManager() {
-        return xml.component.find { it. at name == 'ProjectRootManager'}
-    }
-
-    private def findWildcardResourcePatterns() {
-        xml.component.find { it. at name == 'CompilerConfiguration'}.wildcardResourcePatterns
-    }
-
-    private def findModules() {
-        def moduleManager = xml.component.find { it. at name == 'ProjectModuleManager'}
-        if (!moduleManager.modules) {
-            moduleManager.appendNode('modules')
-        }
-        moduleManager.modules
-    }
-
-    boolean equals(o) {
-        if (this.is(o)) { return true }
-
-        if (getClass() != o.class) { return false }
-
-        Project project = (Project) o;
-
-        if (jdk != project.jdk) { return false }
-        if (modulePaths != project.modulePaths) { return false }
-        if (wildcards != project.wildcards) { return false }
-
-        return true;
-    }
-
-    int hashCode() {
-        int result;
-
-        result = (modulePaths != null ? modulePaths.hashCode() : 0);
-        result = 31 * result + (wildcards != null ? wildcards.hashCode() : 0);
-        result = 31 * result + (jdk != null ? jdk.hashCode() : 0);
-        return result;
-    }
-}
diff --git a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/Workspace.groovy b/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/Workspace.groovy
deleted file mode 100644
index cb27c79..0000000
--- a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/Workspace.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.plugins.idea.model
-
-import org.gradle.api.internal.XmlTransformer
-import org.gradle.api.internal.tasks.generator.XmlPersistableConfigurationObject
-
-/**
- * Represents the customizable elements of an ipr (via XML hooks everything of the ipr is customizable).
- *
- * @author Hans Dockter
- */
-
-class Workspace extends XmlPersistableConfigurationObject {
-    def Workspace(XmlTransformer withXmlActions) {
-        super(withXmlActions)
-    }
-
-    @Override protected String getDefaultResourceName() {
-        return 'defaultWorkspace.xml'
-    }
-
-    @Override protected void load(Node xml) {
-    }
-
-    @Override protected void store(Node xml) {
-    }
-}
\ No newline at end of file
diff --git a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/package-info.java b/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/package-info.java
deleted file mode 100644
index 5d53666..0000000
--- a/subprojects/idea/src/main/groovy/org/gradle/plugins/idea/model/package-info.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.
- */
-
-/**
- * Classes for the model used by the {@link org.gradle.plugins.idea.IdeaPlugin}. 
- */
-package org.gradle.plugins.idea.model;
diff --git a/subprojects/idea/src/main/resources/META-INF/gradle-plugins/idea.properties b/subprojects/idea/src/main/resources/META-INF/gradle-plugins/idea.properties
deleted file mode 100644
index 7845246..0000000
--- a/subprojects/idea/src/main/resources/META-INF/gradle-plugins/idea.properties
+++ /dev/null
@@ -1,16 +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.
-#
-implementation-class=org.gradle.plugins.idea.IdeaPlugin
diff --git a/subprojects/idea/src/main/resources/org/gradle/plugins/idea/model/defaultModule.xml b/subprojects/idea/src/main/resources/org/gradle/plugins/idea/model/defaultModule.xml
deleted file mode 100644
index d89aa25..0000000
--- a/subprojects/idea/src/main/resources/org/gradle/plugins/idea/model/defaultModule.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<module relativePaths="true" type="JAVA_MODULE" version="4">
-    <component name="NewModuleRootManager">
-        <exclude-output/>
-        <orderEntry type="inheritedJdk"/>
-        <content url="file://$MODULE_DIR$">
-        </content>
-        <orderEntry type="sourceFolder" forTests="false"/>
-    </component>
-    <component name="ModuleRootManager"/>
-</module>
-
diff --git a/subprojects/idea/src/main/resources/org/gradle/plugins/idea/package-info.java b/subprojects/idea/src/main/resources/org/gradle/plugins/idea/package-info.java
deleted file mode 100644
index 654d077..0000000
--- a/subprojects/idea/src/main/resources/org/gradle/plugins/idea/package-info.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.
- */
-
-/**
- * A {link org.gradle.api.Plugin} for generating IDEA files. 
- */
-package org.gradle.plugins.idea;
diff --git a/subprojects/idea/src/test/groovy/org/gradle/plugins/idea/IdeaPluginTest.groovy b/subprojects/idea/src/test/groovy/org/gradle/plugins/idea/IdeaPluginTest.groovy
deleted file mode 100644
index 32ea2a1..0000000
--- a/subprojects/idea/src/test/groovy/org/gradle/plugins/idea/IdeaPluginTest.groovy
+++ /dev/null
@@ -1,134 +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.plugins.idea
-
-import org.gradle.api.JavaVersion
-import org.gradle.api.Project
-import org.gradle.api.Task
-import org.gradle.api.internal.project.DefaultProject
-import org.gradle.api.tasks.Delete
-import org.gradle.util.HelperUtil
-import spock.lang.Specification
-
-/**
- * @author Hans Dockter
- */
-class IdeaPluginTest extends Specification {
-    private final DefaultProject project = HelperUtil.createRootProject()
-    private final Project childProject = HelperUtil.createChildProject(project, "child", new File("."))
-    private final IdeaPlugin ideaPlugin = new IdeaPlugin()
-
-    def addsIdeaProjectToRootProject() {
-        when:
-        applyPluginToProjects()
-
-        then:
-        assertThatCleanIdeaDependsOnDeleteTask(project, project.cleanIdeaProject)
-        IdeaProject ideaProjectTask = project.ideaProject
-        ideaProjectTask instanceof IdeaProject
-        ideaProjectTask.outputFile == new File(project.projectDir, project.name + ".ipr")
-        ideaProjectTask.subprojects == project.rootProject.allprojects
-        ideaProjectTask.javaVersion == JavaVersion.VERSION_1_6.toString()
-        ideaProjectTask.wildcards == ['!?*.java', '!?*.groovy'] as Set
-
-        childProject.tasks.findByName('ideaProject') == null
-        childProject.tasks.findByName('cleanIdeaProject') == null
-    }
-
-    def addsIdeaWorkspaceToRootProject() {
-        when:
-        applyPluginToProjects()
-
-        then:
-        project.ideaWorkspace instanceof IdeaWorkspace
-        assertThatCleanIdeaDependsOnDeleteTask(project, project.cleanIdeaWorkspace)
-
-        childProject.tasks.findByName('ideaWorkspace') == null
-        childProject.tasks.findByName('cleanIdeaWorkspace') == null
-    }
-
-    def addsIdeaModuleToProjects() {
-        when:
-        applyPluginToProjects()
-
-        then:
-        assertThatIdeaModuleIsProperlyConfigured(project)
-        assertThatIdeaModuleIsProperlyConfigured(childProject)
-    }
-
-    def addsSpecialConfigurationIfJavaPluginIsApplied() {
-        when:
-        applyPluginToProjects()
-        project.apply(plugin: 'java')
-
-        then:
-        project.ideaProject.javaVersion == project.sourceCompatibility.toString()
-
-        IdeaModule ideaModuleTask = project.ideaModule
-        ideaModuleTask.sourceDirs == project.sourceSets.main.allSource.sourceTrees.srcDirs.flatten() as Set
-        ideaModuleTask.testSourceDirs == project.sourceSets.test.allSource.sourceTrees.srcDirs.flatten() as Set
-        ideaModuleTask.outputDir == project.file("out/production/$project.name")
-        ideaModuleTask.testOutputDir == project.file("out/test/$project.name")
-        def configurations = project.configurations
-        ideaModuleTask.scopes == [
-                COMPILE: [plus: [configurations.compile], minus: []],
-                RUNTIME: [plus: [configurations.runtime], minus: [configurations.compile]],
-                TEST: [plus: [configurations.testRuntime], minus: [configurations.runtime]]
-        ]
-    }
-
-    void assertThatIdeaModuleIsProperlyConfigured(Project project) {
-        IdeaModule ideaModuleTask = project.ideaModule
-        assert ideaModuleTask instanceof IdeaModule
-        assert ideaModuleTask.outputFile == new File(project.projectDir, project.name + ".iml")
-        assert ideaModuleTask.moduleDir == project.projectDir
-        assert ideaModuleTask.sourceDirs == [] as Set
-        assert ideaModuleTask.testSourceDirs == [] as Set
-        assert ideaModuleTask.excludeDirs == [project.buildDir, project.file('.gradle')] as Set
-        assert ideaModuleTask.variables == [:]
-        assertThatCleanIdeaDependsOnDeleteTask(project, project.cleanIdeaModule)
-    }
-
-    void shouldPickUpLateChangesToBuildDir() {
-        when:
-        applyPluginToProjects()
-        project.apply(plugin: 'java')
-        project.buildDir = project.file('target')
-
-        then:
-        project.ideaModule.excludeDirs == [project.buildDir, project.file('.gradle')] as Set
-        project.ideaModule.outputDir == project.file("out/production/$project.name")
-    }
-
-    void assertThatCleanIdeaDependsOnDeleteTask(Project project, Task dependsOnTask) {
-        assert dependsOnTask instanceof Delete
-        assert project.cleanIdea.taskDependencies.getDependencies(project.cleanIdea).contains(dependsOnTask)
-    }
-
-    def addsCleanIdeaToProjects() {
-        when:
-        applyPluginToProjects()
-
-        then:
-        project.cleanIdea instanceof Task
-        childProject.cleanIdea instanceof Task
-    }
-
-    private def applyPluginToProjects() {
-        ideaPlugin.apply(project)
-        ideaPlugin.apply(childProject)
-    }
-}
\ No newline at end of file
diff --git a/subprojects/idea/src/test/groovy/org/gradle/plugins/idea/model/ModuleDependencyTest.groovy b/subprojects/idea/src/test/groovy/org/gradle/plugins/idea/model/ModuleDependencyTest.groovy
deleted file mode 100644
index 8198027..0000000
--- a/subprojects/idea/src/test/groovy/org/gradle/plugins/idea/model/ModuleDependencyTest.groovy
+++ /dev/null
@@ -1,51 +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.plugins.idea.model
-
-import spock.lang.Specification
-
-/**
- * @author Hans Dockter
- */
-class ModuleDependencyTest extends Specification {
-    def equality() {
-        expect:
-        new ModuleDependency("a", null).equals(new ModuleDependency("a", "COMPILE"))
-        new ModuleDependency("a", "").equals(new ModuleDependency("a", "COMPILE"))
-        new ModuleDependency("a", "COMPILE").equals(new ModuleDependency("a", ""))
-        new ModuleDependency("a", "COMPILE").equals(new ModuleDependency("a", null))
-        new ModuleDependency("a", "").equals(new ModuleDependency("a", ""))
-        !new ModuleDependency("a", "").equals(new ModuleDependency("b", ""))
-        !new ModuleDependency("a", "RUNTIME").equals(new ModuleDependency("a", "COMPILE"))
-        !new ModuleDependency("a", "").equals(new ModuleDependency("a", "RUNTIME"))
-        !new ModuleDependency("a", "RUNTIME").equals(new ModuleDependency("a", ""))
-    }
-
-    def hash() {
-        expect:
-        new ModuleDependency("a", null).hashCode() == new ModuleDependency("a", "COMPILE").hashCode()
-        new ModuleDependency("a", "").hashCode() == new ModuleDependency("a", "COMPILE").hashCode()
-    }
-
-    def shouldExportForCompileAndRuntimeScope() {
-        expect:
-        new ModuleDependency("a", "COMPILE").exported
-        new ModuleDependency("a", "RUNTIME").exported
-        new ModuleDependency("a", "").exported
-        new ModuleDependency("a", null).exported
-        !(new ModuleDependency("a", "TEST").exported)
-    }
-}
\ No newline at end of file
diff --git a/subprojects/idea/src/test/groovy/org/gradle/plugins/idea/model/ModuleLibraryTest.groovy b/subprojects/idea/src/test/groovy/org/gradle/plugins/idea/model/ModuleLibraryTest.groovy
deleted file mode 100644
index 4936ac3..0000000
--- a/subprojects/idea/src/test/groovy/org/gradle/plugins/idea/model/ModuleLibraryTest.groovy
+++ /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.plugins.idea.model
-
-import spock.lang.Specification
-
-/**
- * @author Hans Dockter
- */
-class ModuleLibraryTest extends Specification {
-    def equality() {
-        expect:
-        createModuleLibraryWithScope(null).equals(createModuleLibraryWithScope("COMPILE"))
-        createModuleLibraryWithScope("").equals(createModuleLibraryWithScope("COMPILE"))
-        createModuleLibraryWithScope("COMPILE").equals(createModuleLibraryWithScope(null))
-        createModuleLibraryWithScope("COMPILE").equals(createModuleLibraryWithScope(""))
-        createModuleLibraryWithScope("").equals(createModuleLibraryWithScope(""))
-        !createModuleLibraryWithScope("RUNTIME").equals(createModuleLibraryWithScope("COMPILE"))
-        !createModuleLibraryWithScope("").equals(createModuleLibraryWithScope("RUNTIME"))
-        !createModuleLibraryWithScope("RUNTIME").equals(createModuleLibraryWithScope(""))
-    }
-
-    def hash() {
-        expect:
-        createModuleLibraryWithScope(null).hashCode() == createModuleLibraryWithScope("COMPILE").hashCode()
-        createModuleLibraryWithScope("").hashCode() == createModuleLibraryWithScope("COMPILE").hashCode()
-    }
-
-    private ModuleLibrary createModuleLibraryWithScope(String scope) {
-        new ModuleLibrary([] as Set, [] as Set, [] as Set, [] as Set, scope)
-    }
-
-    def shouldExportForCompileAndRuntimeScope() {
-        expect:
-        createModuleLibraryWithScope("COMPILE").exported
-        createModuleLibraryWithScope("RUNTIME").exported
-        createModuleLibraryWithScope("").exported
-        createModuleLibraryWithScope(null).exported
-        !(createModuleLibraryWithScope("TEST").exported)
-    }
-}
\ No newline at end of file
diff --git a/subprojects/idea/src/test/groovy/org/gradle/plugins/idea/model/ModulePathTest.groovy b/subprojects/idea/src/test/groovy/org/gradle/plugins/idea/model/ModulePathTest.groovy
deleted file mode 100644
index 01ad10e..0000000
--- a/subprojects/idea/src/test/groovy/org/gradle/plugins/idea/model/ModulePathTest.groovy
+++ /dev/null
@@ -1,31 +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.plugins.idea.model
-
-import spock.lang.Specification
-import org.gradle.util.Matchers
-
-class ModulePathTest extends Specification {
-    def pathsAreEqualWhenTheirPathAndFilePathAreEqual() {
-        Path path = new Path('url')
-        String filePath = 'path'
-
-        expect:
-        Matchers.strictlyEquals(new ModulePath(path, filePath), new ModulePath(path, filePath))
-        new ModulePath(path, filePath) != new ModulePath(path, 'other')
-        new ModulePath(path, filePath) != new ModulePath(new Path('other'), filePath)
-    }
-}
diff --git a/subprojects/idea/src/test/groovy/org/gradle/plugins/idea/model/ModuleTest.groovy b/subprojects/idea/src/test/groovy/org/gradle/plugins/idea/model/ModuleTest.groovy
deleted file mode 100644
index 7f411e1..0000000
--- a/subprojects/idea/src/test/groovy/org/gradle/plugins/idea/model/ModuleTest.groovy
+++ /dev/null
@@ -1,118 +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.plugins.idea.model
-
-import org.gradle.api.internal.XmlTransformer
-import spock.lang.Specification
-
-/**
- * @author Hans Dockter
- */
-class ModuleTest extends Specification {
-    final PathFactory pathFactory = new PathFactory()
-    final XmlTransformer xmlTransformer = new XmlTransformer()
-    final customSourceFolders = [path('file://$MODULE_DIR$/src')] as LinkedHashSet
-    final customTestSourceFolders = [path('file://$MODULE_DIR$/srcTest')] as LinkedHashSet
-    final customExcludeFolders = [path('file://$MODULE_DIR$/target')] as LinkedHashSet
-    final customDependencies = [
-            new ModuleLibrary([path('file://$MODULE_DIR$/gradle/lib')] as Set,
-                    [path('file://$MODULE_DIR$/gradle/javadoc')] as Set, [path('file://$MODULE_DIR$/gradle/src')] as Set,
-                    [] as Set, null),
-            new ModuleLibrary([path('file://$MODULE_DIR$/ant/lib'), path('jar://$GRADLE_CACHE$/gradle.jar!/')] as Set, [] as Set, [] as Set,
-                    [new JarDirectory(path('file://$MODULE_DIR$/ant/lib'), false)] as Set, "RUNTIME"),
-            new ModuleDependency('someModule', null)]
-
-    Module module = new Module(xmlTransformer, pathFactory)
-
-    def loadFromReader() {
-        when:
-        module.load(customModuleReader)
-
-        then:
-        module.javaVersion == "1.6"
-        module.sourceFolders == customSourceFolders
-        module.testSourceFolders == customTestSourceFolders
-        module.excludeFolders == customExcludeFolders
-        module.outputDir == path('file://$MODULE_DIR$/out')
-        module.testOutputDir == path('file://$MODULE_DIR$/outTest')
-        (module.dependencies as List) == customDependencies
-    }
-
-    def configureOverwritesDependenciesAndAppendsAllOtherEntries() {
-        def constructorSourceFolders = [path('a')] as Set
-        def constructorTestSourceFolders = [path('b')] as Set
-        def constructorExcludeFolders = [path('c')] as Set
-        def constructorOutputDir = path('someOut')
-        def constructorJavaVersion = '1.6'
-        def constructorTestOutputDir = path('someTestOut')
-        def constructorModuleDependencies = [
-                customDependencies[0],
-                new ModuleLibrary([path('x')], [], [], [new JarDirectory(path('y'), false)], null)] as LinkedHashSet
-
-        when:
-        module.load(customModuleReader)
-        module.configure(null, constructorSourceFolders, constructorTestSourceFolders, constructorExcludeFolders, constructorOutputDir, constructorTestOutputDir, constructorModuleDependencies, constructorJavaVersion)
-
-        then:
-        module.sourceFolders == customSourceFolders + constructorSourceFolders
-        module.testSourceFolders == customTestSourceFolders + constructorTestSourceFolders
-        module.excludeFolders == customExcludeFolders + constructorExcludeFolders
-        module.outputDir == constructorOutputDir
-        module.testOutputDir == constructorTestOutputDir
-        module.javaVersion == constructorJavaVersion
-        module.dependencies == constructorModuleDependencies
-    }
-
-    def loadDefaults() {
-        when:
-        module.loadDefaults()
-
-        then:
-        module.javaVersion == Module.INHERITED
-        module.sourceFolders == [] as Set
-        module.dependencies.size() == 0
-    }
-
-    def generatedXmlShouldContainCustomValues() {
-        def constructorSourceFolders = [new Path('a')] as Set
-        def constructorOutputDir = new Path('someOut')
-        def constructorTestOutputDir = new Path('someTestOut')
-
-        when:
-        module.loadDefaults()
-        module.configure(null, constructorSourceFolders, [] as Set, [] as Set, constructorOutputDir, constructorTestOutputDir, [] as Set, null)
-        def xml = toXmlReader
-        def newModule = new Module(xmlTransformer, pathFactory)
-        newModule.load(xml)
-
-        then:
-        this.module == newModule
-    }
-
-    private InputStream getToXmlReader() {
-        ByteArrayOutputStream toXmlText = new ByteArrayOutputStream()
-        module.store(toXmlText)
-        return new ByteArrayInputStream(toXmlText.toByteArray())
-    }
-
-    private InputStream getCustomModuleReader() {
-        return getClass().getResourceAsStream('customModule.xml')
-    }
-
-    private Path path(String url) {
-        pathFactory.path(url)
-    }
-}
\ No newline at end of file
diff --git a/subprojects/idea/src/test/groovy/org/gradle/plugins/idea/model/PathFactoryTest.groovy b/subprojects/idea/src/test/groovy/org/gradle/plugins/idea/model/PathFactoryTest.groovy
deleted file mode 100644
index c0c611a..0000000
--- a/subprojects/idea/src/test/groovy/org/gradle/plugins/idea/model/PathFactoryTest.groovy
+++ /dev/null
@@ -1,170 +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.plugins.idea.model
-
-import org.gradle.util.TemporaryFolder
-import org.junit.Rule
-import spock.lang.Specification
-import org.gradle.util.TestFile
-
-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 = relpath(file)
-
-        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 rootDir = factory.path(tmpDir.dir)
-        rootDir.url == 'file://$ROOT_DIR$/'
-
-        def subDir = factory.path(tmpDir.file('sub'))
-        subDir.url == 'file://$SUB_DIR$/'
-    }
-
-    def createsPathForAJarFile() {
-        factory.addPathVariable('ROOT_DIR', tmpDir.dir)
-
-        expect:
-        def path = factory.path(tmpDir.file('a.jar'))
-        path.url == 'jar://$ROOT_DIR$/a.jar!/'
-    }
-
-    def createsRelativePath() {
-        factory.addPathVariable('ROOT_DIR', tmpDir.dir)
-
-        expect:
-        def path = factory.relativePath('ROOT_DIR', tmpDir.file('a/b'))
-        path.url == 'file://$ROOT_DIR$/a/b'
-
-        def parentPath = factory.relativePath('ROOT_DIR', tmpDir.dir.parentFile.parentFile.file('a/b'))
-        parentPath.url == 'file://$ROOT_DIR$/../../a/b'
-    }
-    
-    def createsPathForAFileUrl() {
-        expect:
-        def path = factory.path('file://a/b/c')
-        path.url == 'file://a/b/c'
-    }
-
-    def createsPathForAJarUrl() {
-        expect:
-        def path = factory.path('jar://a/b/c.jar!/some/entry')
-        path.url == 'jar://a/b/c.jar!/some/entry'
-    }
-
-    def createsPathForAUrlWithUnknownScheme() {
-        expect:
-        def path = factory.path('other:abc')
-        path.url == 'other:abc'
-    }
-
-    def createsPathForAUrlWithPathVariables() {
-        factory.addPathVariable('ROOT_DIR', tmpDir.dir)
-
-        expect:
-        def path = factory.path('file://$ROOT_DIR$/c')
-        path.url == 'file://$ROOT_DIR$/c'
-    }
-
-    def filePathsAreEqualWhenTheyPointToTheSameFile() {
-        TestFile subDir = tmpDir.file('sub')
-        TestFile childFile = tmpDir.file('sub/a/b')
-
-        factory.addPathVariable('SUB_DIR', subDir)
-        factory.addPathVariable('ROOT_DIR', tmpDir.dir)
-
-        expect:
-
-        // Using files
-        factory.path(subDir) == factory.path(subDir)
-        factory.path(childFile) == factory.path(childFile)
-        factory.path(childFile) != factory.path(subDir)
-
-        // Using normalised absolute urls
-        factory.path(subDir) == factory.path("file://${relpath(subDir)}")
-        factory.path(subDir) == factory.path("file://${relpath(childFile)}/../..")
-        factory.path("file://${relpath(subDir)}") != factory.path("file://${relpath(childFile)}")
-
-        // Using absolute paths
-        factory.path(subDir) == factory.path("file://${subDir.absolutePath}")
-
-        // Using replacement variables
-        factory.path(childFile) == factory.path('file://$SUB_DIR$/a/b')
-        factory.path(childFile) == factory.path('file://$SUB_DIR$/c/../a/b')
-        factory.path('file://$ROOT_DIR$/sub') == factory.path('file://$SUB_DIR$')
-        factory.path('file://$ROOT_DIR$') != factory.path('file://$SUB_DIR$')
-    }
-
-    def filePathsAreEqualWhenTheyPointToTheSameEntryInTheSameFile() {
-        TestFile subDir = tmpDir.file('sub')
-        TestFile childFile = tmpDir.file('sub/a/b.jar')
-
-        factory.addPathVariable('SUB_DIR', subDir)
-        factory.addPathVariable('ROOT_DIR', tmpDir.dir)
-
-        expect:
-
-        // Using files
-        factory.path(childFile) == factory.path(childFile)
-        factory.path(childFile) != factory.path(subDir)
-
-        // Using normalised absolute urls
-        factory.path(childFile) == factory.path("jar://${relpath(childFile)}!/")
-        factory.path("jar://${relpath(childFile)}!/entry") == factory.path("jar://${relpath(childFile)}!/entry")
-        factory.path(childFile) != factory.path("jar://${relpath(childFile)}!/entry")
-
-        // Using replacement variables
-        factory.path(childFile) == factory.path('jar://$SUB_DIR$/a/b.jar!/')
-        factory.path(childFile) == factory.path('jar://$SUB_DIR$/c/../a/b.jar!/')
-
-        factory.path(childFile) != factory.path('jar://$SUB_DIR$/a/b.jar')
-        factory.path(childFile) != factory.path("file://${relpath(childFile)}")
-    }
-
-    private String relpath(File file) {
-        return file.absolutePath.replace(File.separator, '/')
-    }
-
-}
diff --git a/subprojects/idea/src/test/groovy/org/gradle/plugins/idea/model/PathTest.groovy b/subprojects/idea/src/test/groovy/org/gradle/plugins/idea/model/PathTest.groovy
deleted file mode 100644
index 17596cc..0000000
--- a/subprojects/idea/src/test/groovy/org/gradle/plugins/idea/model/PathTest.groovy
+++ /dev/null
@@ -1,113 +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.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 pathsAreEqualWhenTheyHaveTheSameCanonicalUrl() {
-        expect:
-        Matchers.strictlyEquals(new Path('file://$ROOT_DIR$/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/idea/src/test/groovy/org/gradle/plugins/idea/model/ProjectTest.groovy b/subprojects/idea/src/test/groovy/org/gradle/plugins/idea/model/ProjectTest.groovy
deleted file mode 100644
index ac6f32a..0000000
--- a/subprojects/idea/src/test/groovy/org/gradle/plugins/idea/model/ProjectTest.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.plugins.idea.model
-
-import org.gradle.api.internal.XmlTransformer
-import spock.lang.Specification
-
-/**
- * @author Hans Dockter
- */
-class ProjectTest extends Specification {
-    final PathFactory pathFactory = new PathFactory()
-    final customModules = [new ModulePath(path('file://$PROJECT_DIR$/gradle-idea-plugin.iml'), '$PROJECT_DIR$/gradle-idea-plugin.iml')] as Set
-    final customWildcards = ["?*.gradle", "?*.grails"] as Set
-    Project project = new Project(new XmlTransformer(), pathFactory)
-
-    def loadFromReader() {
-        when:
-        project.load(customProjectReader)
-
-        then:
-        project.modulePaths == customModules
-        project.wildcards == customWildcards
-        project.jdk == new Jdk(true, false, null, "1.4")
-    }
-
-    def customJdkAndWildcards_shouldBeMerged() {
-        def modules = [new ModulePath(path('file://$PROJECT_DIR$/other.iml'), '$PROJECT_DIR$/other.iml')] as Set
-
-        when:
-        project.load(customProjectReader)
-        project.configure(modules, '1.6', ['?*.groovy'] as Set)
-
-        then:
-        project.modulePaths == customModules + modules
-        project.wildcards == customWildcards + ['?*.groovy'] as Set
-        project.jdk == new Jdk("1.6")
-    }
-
-    def loadDefaults() {
-        when:
-        project.loadDefaults()
-
-        then:
-        project.modulePaths.size() == 0
-        project.wildcards == [] as Set
-        project.jdk == new Jdk(true, true, "JDK_1_5", null)
-    }
-
-    def toXml_shouldContainCustomValues() {
-        when:
-        project.loadDefaults()
-        project.configure([] as Set, '1.5', ['?*.groovy'] as Set)
-        def xml = toXmlReader
-        def other = new Project(new XmlTransformer(), pathFactory)
-        other.load(xml)
-
-        then:
-        project.wildcards == other.wildcards
-        project.modulePaths == other.modulePaths
-        project.jdk == other.jdk
-    }
-
-    private InputStream getToXmlReader() {
-        ByteArrayOutputStream toXmlText = new ByteArrayOutputStream()
-        project.store(toXmlText)
-        return new ByteArrayInputStream(toXmlText.toByteArray())
-    }
-
-    private InputStream getCustomProjectReader() {
-        return getClass().getResourceAsStream('customProject.xml')
-    }
-
-    private Path path(String url) {
-        pathFactory.path(url)
-    }
-}
diff --git a/subprojects/integ-test/integ-test.gradle b/subprojects/integ-test/integ-test.gradle
new file mode 100644
index 0000000..d8136bc
--- /dev/null
+++ b/subprojects/integ-test/integ-test.gradle
@@ -0,0 +1,36 @@
+apply plugin: 'groovy'
+apply from: "$rootDir/gradle/integTest.gradle"
+
+dependencies {
+    groovy libraries.groovy_depends
+
+    // TODO - don't include the runtime at compile time. This is here because the Groovy compiler needs the runtime classpath
+//    integTestCompile project(path: ':core', configuration: 'integTestFixtures')
+//    integTestRuntime project(path: ':core', configuration: 'integTestFixturesRuntime')
+    integTestCompile project(path: ':core', configuration: 'integTestFixturesRuntime')
+
+    integTestCompile project(':toolingApi')
+    integTestCompile libraries.ant
+    integTestCompile libraries.xmlunit
+
+    integTestRuntime rootProject.configurations.testRuntime.allDependencies
+}
+
+integTestTasks.all {
+    dependsOn ':publishLocalArchives', ':binZip', ':allZip', ':srcZip', ':docs:userguideDocbook'
+
+    jvmArgs '-Xmx512m', '-XX:MaxPermSize=256m'
+
+    doFirst {
+        systemProperties['integTest.userGuideInfoDir'] = project(':docs').docbookSrc
+        systemProperties['integTest.userGuideOutputDir'] = new File(project(':docs').samplesSrcDir, "userguideOutput").absolutePath
+        systemProperties['integTest.distsDir'] = rootProject.distsDir.absolutePath
+        systemProperties['integTest.libsRepo'] = rootProject.file('build/repo')
+        forkEvery = 15
+        maxParallelForks = guessMaxForks()
+
+        if (isDevBuild()) {
+            exclude 'org/gradle/integtests/DistributionIntegrationTest.*'
+        }
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/AntProjectIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/AntProjectIntegrationTest.groovy
new file mode 100644
index 0000000..87d44a7
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/AntProjectIntegrationTest.groovy
@@ -0,0 +1,158 @@
+/*
+ * 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.integtests.fixtures.ExecutionFailure
+import org.gradle.util.TestFile
+import org.junit.Test
+import static org.hamcrest.Matchers.*
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest
+
+public class AntProjectIntegrationTest extends AbstractIntegrationTest {
+    @Test
+    public void antTargetsAndGradleTasksCanDependOnEachOther() {
+        testFile('build.xml') << """
+<project>
+    <target name='target1' depends='target2,init'>
+        <touch file='build/target1.txt'/>
+    </target>
+    <target name='target2' depends='init'>
+        <touch file='build/target2.txt'/>
+    </target>
+</project>
+"""
+        testFile('build.gradle') << """
+ant.importBuild(file('build.xml'))
+task init << { buildDir.mkdirs() }
+task ant(dependsOn: target1)
+"""
+        TestFile target1File = testFile('build/target1.txt')
+        TestFile target2File = testFile('build/target2.txt')
+        target1File.assertDoesNotExist()
+        target2File.assertDoesNotExist()
+
+        inTestDirectory().withTasks('ant').run().assertTasksExecuted(':init', ':target2', ':target1', ':ant')
+
+        target1File.assertExists()
+        target2File.assertExists()
+    }
+
+    @Test
+    public void canImportMultipleBuildFilesWithDifferentBaseDirs() {
+        testFile('project1/build.xml') << """
+<project>
+    <target name='target1'>
+        <mkdir dir='build'/>
+        <touch file='build/target1.txt'/>
+    </target>
+</project>
+"""
+        testFile('project2/build.xml') << """
+<project>
+    <target name='target2'>
+        <mkdir dir='build'/>
+        <touch file='build/target2.txt'/>
+    </target>
+</project>
+"""
+        testFile('build.gradle') << """
+ant.importBuild('project1/build.xml')
+ant.importBuild('project2/build.xml')
+task ant(dependsOn: [target1, target2])
+"""
+        TestFile target1File = testFile('project1/build/target1.txt')
+        TestFile target2File = testFile('project2/build/target2.txt')
+        target1File.assertDoesNotExist()
+        target2File.assertDoesNotExist()
+
+        inTestDirectory().withTasks('ant').run().assertTasksExecuted(':target1', ':target2', ':ant')
+
+        target1File.assertExists()
+        target2File.assertExists()
+    }
+
+    @Test
+    public void handlesAntImportsOk() {
+        testFile('imported.xml') << """
+<project>
+    <target name='target1'>
+        <mkdir dir='build'/>
+        <touch file='build/target1.txt'/>
+    </target>
+</project>
+"""
+        testFile('build.xml') << """
+<project>
+    <import file="imported.xml"/>
+    <target name='target2'>
+        <mkdir dir='build'/>
+        <touch file='build/target2.txt'/>
+    </target>
+</project>
+"""
+        testFile('build.gradle') << """
+ant.importBuild('build.xml')
+task ant(dependsOn: [target1, target2])
+"""
+        TestFile target1File = testFile('build/target1.txt')
+        TestFile target2File = testFile('build/target2.txt')
+        target1File.assertDoesNotExist()
+        target2File.assertDoesNotExist()
+
+        inTestDirectory().withTasks('ant').run().assertTasksExecuted(':target1', ':target2', ':ant')
+
+        target1File.assertExists()
+        target2File.assertExists()
+    }
+
+    @Test
+    public void reportsAntBuildParseFailure() {
+        TestFile antBuildFile = testFile('build.xml')
+        antBuildFile << """
+<project>
+    <target name='target1'
+        <unknown/>
+    </target>
+</project>
+"""
+        TestFile buildFile = testFile('build.gradle')
+        buildFile << """
+ant.importBuild('build.xml')
+"""
+        ExecutionFailure failure = inTestDirectory().withTasks('target1').runWithFailure()
+        failure.assertHasFileName("Build file '$buildFile'")
+        failure.assertThatDescription(startsWith('A problem occurred evaluating root project'))
+        failure.assertHasCause("Could not import Ant build file '$antBuildFile'.")
+    }
+
+    @Test
+    public void reportsAntTaskExecutionFailure() {
+        testFile('build.xml') << """
+<project>
+    <target name='target1'>
+        <fail>broken</fail>
+    </target>
+</project>
+"""
+        TestFile buildFile = testFile('build.gradle')
+        buildFile << """
+ant.importBuild('build.xml')
+"""
+        ExecutionFailure failure = inTestDirectory().withTasks('target1').runWithFailure()
+        failure.assertHasDescription('Execution failed for task \':target1\'.')
+        failure.assertHasCause('broken')
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/AntlrIntegrationTest.java b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/AntlrIntegrationTest.java
new file mode 100644
index 0000000..42227b4
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/AntlrIntegrationTest.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;
+
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest;
+import org.junit.Test;
+
+public class AntlrIntegrationTest extends AbstractIntegrationTest {
+    @Test
+    public void handlesEmptyProject() {
+        testFile("build.gradle").writelns("apply plugin: 'antlr'");
+        inTestDirectory().withTasks("build").run();
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ApplicationIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ApplicationIntegrationTest.groovy
new file mode 100644
index 0000000..94cc313
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ApplicationIntegrationTest.groovy
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.gradle.integtests.fixtures.GradleExecuter
+import org.gradle.integtests.fixtures.ScriptExecuter
+import org.gradle.util.TestFile
+import org.junit.Rule
+import spock.lang.Specification
+
+import static org.hamcrest.Matchers.*
+
+class ApplicationIntegrationTest extends Specification {
+    @Rule public final GradleDistribution distribution = new GradleDistribution()
+    @Rule public final GradleExecuter executer = new GradleDistributionExecuter()
+
+    def canUseEnvironmentVariableToPassOptionsToJvmWhenRunningScript() {
+        distribution.testFile('build.gradle') << '''
+apply plugin: 'application'
+mainClassName = 'org.gradle.test.Main'
+applicationName = 'application'
+'''
+        distribution.testFile('src/main/java/org/gradle/test/Main.java') << '''
+package org.gradle.test;
+
+class Main {
+    public static void main(String[] args) {
+        if (System.getProperty("testValue") == null) {
+            throw new RuntimeException("Expected system property not specified");
+        }
+    }
+}
+'''
+
+        when:
+        executer.withTasks('install').run()
+
+        def builder = new ScriptExecuter()
+        builder.workingDir distribution.testDir.file('build/install/application/bin')
+        builder.executable "application"
+        builder.environment('APPLICATION_OPTS', '-DtestValue=value')
+
+        def result = builder.run()
+
+        then:
+        result.assertNormalExitValue()
+    }
+
+    def "can customize application name"() {
+        distribution.testFile('settings.gradle') << 'rootProject.name = "application"'
+        distribution.testFile('build.gradle') << '''
+apply plugin: 'application'
+mainClassName = 'org.gradle.test.Main'
+applicationName = 'mega-app'
+'''
+        distribution.testFile('src/main/java/org/gradle/test/Main.java') << '''
+package org.gradle.test;
+
+class Main {
+    public static void main(String[] args) {
+    }
+}
+'''
+
+        when:
+        executer.withTasks('install', 'distZip').run()
+
+        then:
+        def installDir = distribution.testFile('build/install/mega-app')
+        installDir.assertIsDir()
+        checkApplicationImage(installDir)
+
+        def distFile = distribution.testFile('build/distributions/mega-app.zip')
+        distFile.assertIsFile()
+
+        def distDir = distribution.testFile('build/unzip')
+        distFile.usingNativeTools().unzipTo(distDir)
+        checkApplicationImage(distDir.file('mega-app'))
+    }
+
+    def "installApp complains if install directory exists and doesn't look like previous install"() {
+        distribution.testFile('build.gradle') << '''
+apply plugin: 'application'
+mainClassName = 'org.gradle.test.Main'
+installApp.destinationDir = buildDir
+'''
+
+        when:
+        def result = executer.withTasks('installApp').runWithFailure()
+
+        then:
+        result.assertThatCause(startsWith("The specified installation directory '${distribution.testFile('build')}' is neither empty nor does it contain an installation"))
+    }
+
+    private void checkApplicationImage(TestFile installDir) {
+        installDir.file('bin/mega-app').assertIsFile()
+        installDir.file('bin/mega-app.bat').assertIsFile()
+        installDir.file('lib/application.jar').assertIsFile()
+
+        def builder = new ScriptExecuter()
+        builder.workingDir installDir.file('bin')
+        builder.executable 'mega-app'
+        builder.standardOutput = new ByteArrayOutputStream()
+        builder.errorOutput = new ByteArrayOutputStream()
+
+        def result = builder.run()
+        result.assertNormalExitValue()
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ArchiveIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ArchiveIntegrationTest.groovy
new file mode 100644
index 0000000..b9a2202
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ArchiveIntegrationTest.groovy
@@ -0,0 +1,662 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.util.TestFile
+import org.junit.Test
+import static org.junit.Assert.*
+import static org.hamcrest.Matchers.*
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest
+
+public class ArchiveIntegrationTest extends AbstractIntegrationTest {
+    @Test public void canCopyFromAZip() {
+        createZip('test.zip') {
+            subdir1 {
+                file 'file1.txt'
+            }
+            subdir2 {
+                file 'file2.txt'
+                file 'file2.xml'
+            }
+        }
+
+        testFile('build.gradle') << '''
+            task copy(type: Copy) {
+                from zipTree('test.zip')
+                exclude '**/*.xml'
+                into 'dest'
+            }
+'''
+
+        inTestDirectory().withTasks('copy').run()
+
+        testFile('dest').assertHasDescendants('subdir1/file1.txt', 'subdir2/file2.txt')
+    }
+
+    @Test public void canCopyFromATar() {
+        createTar('test.tar') {
+            subdir1 {
+                file 'file1.txt'
+            }
+            subdir2 {
+                file 'file2.txt'
+                file 'file2.xml'
+            }
+        }
+
+        testFile('build.gradle') << '''
+            task copy(type: Copy) {
+                from tarTree('test.tar')
+                exclude '**/*.xml'
+                into 'dest'
+            }
+'''
+
+        inTestDirectory().withTasks('copy').run()
+
+        testFile('dest').assertHasDescendants('subdir1/file1.txt', 'subdir2/file2.txt')
+    }
+
+    @Test public void cannotCreateAnEmptyZip() {
+        testFile('build.gradle') << '''
+            task zip(type: Zip) {
+                from 'test'
+                destinationDir = buildDir
+                archiveName = 'test.zip'
+}
+'''
+
+        inTestDirectory().withTasks('zip').run()
+
+        testFile('build/test.zip').assertDoesNotExist()
+    }
+
+    @Test public void canCreateAnEmptyJar() {
+        testFile('build.gradle') << '''
+            task jar(type: Jar) {
+                from 'test'
+                destinationDir = buildDir
+                archiveName = 'test.jar'
+}
+'''
+
+        inTestDirectory().withTasks('jar').run()
+
+        TestFile expandDir = testFile('expanded')
+        testFile('build/test.jar').unzipTo(expandDir)
+        expandDir.assertHasDescendants('META-INF/MANIFEST.MF')
+
+        expandDir.file('META-INF/MANIFEST.MF').assertContents(equalTo('Manifest-Version: 1.0\r\n\r\n'))
+    }
+
+    @Test public void cannotCreateAnEmptyTar() {
+        testFile('build.gradle') << '''
+            task tar(type: Tar) {
+                from 'test'
+                destinationDir = buildDir
+                archiveName = 'test.tar'
+}
+'''
+
+        inTestDirectory().withTasks('tar').run()
+
+        testFile('build/test.tar').assertDoesNotExist()
+    }
+
+    @Test public void canCreateAZipArchive() {
+        createDir('test') {
+            dir1 {
+                file('file1.txt').write("abc")
+            }
+            file 'file1.txt'
+            dir2 {
+                file 'file2.txt'
+                file 'script.sh'
+            }
+        }
+
+        testFile('build.gradle') << '''
+            task zip(type: Zip) {
+                into('prefix') {
+                    from 'test'
+                    include '**/*.txt'
+                    rename { "renamed_$it" }
+                    filter { "[$it]" }
+                }
+                into('scripts') {
+                    from 'test'
+                    include '**/*.sh'
+                    dirMode = 0750
+                    fileMode = 0754
+                }
+                destinationDir = buildDir
+                archiveName = 'test.zip'
+            }
+'''
+
+        inTestDirectory().withTasks('zip').run()
+
+        TestFile expandDir = testFile('expanded')
+        testFile('build/test.zip').usingNativeTools().unzipTo(expandDir)
+        expandDir.assertHasDescendants(
+                'prefix/dir1/renamed_file1.txt',
+                'prefix/renamed_file1.txt',
+                'prefix/dir2/renamed_file2.txt',
+                'scripts/dir2/script.sh')
+
+        expandDir.file('prefix/dir1/renamed_file1.txt').assertContents(equalTo('[abc]'))
+
+        expandDir.file('prefix/dir1').assertPermissions(equalTo("rwxr-xr-x"))
+        expandDir.file('prefix/dir1/renamed_file1.txt').assertPermissions(equalTo("rw-r--r--"))
+        expandDir.file('scripts/dir2').assertPermissions(equalTo("rwxr-x---"))
+        expandDir.file('scripts/dir2/script.sh').assertPermissions(equalTo("rwxr-xr--"))
+    }
+
+    @Test public void canCreateATarArchive() {
+        createDir('test') {
+            dir1 {
+                file('file1.txt') << 'abc'
+            }
+            file 'file1.txt'
+            dir2 {
+                file 'file2.txt'
+                file 'script.sh'
+            }
+        }
+
+        testFile('build.gradle') << '''
+            task tar(type: Tar) {
+                from('test') {
+                    include '**/*.txt'
+                    filter { "[$it]" }
+                }
+                from('test') {
+                    include '**/*.sh'
+                    into 'scripts'
+                    fileMode = 0754
+                    dirMode = 0750
+                }
+                destinationDir = buildDir
+                archiveName = 'test.tar'
+            }
+'''
+
+        inTestDirectory().withTasks('tar').run()
+
+        TestFile expandDir = testFile('expanded')
+        testFile('build/test.tar').usingNativeTools().untarTo(expandDir)
+        expandDir.assertHasDescendants('dir1/file1.txt', 'file1.txt', 'dir2/file2.txt', 'scripts/dir2/script.sh')
+
+        expandDir.file('dir1/file1.txt').assertContents(equalTo('[abc]'))
+
+        expandDir.file('dir1').assertPermissions(equalTo("rwxr-xr-x"))
+        expandDir.file('dir1/file1.txt').assertPermissions(equalTo("rw-r--r--"))
+        expandDir.file('scripts/dir2').assertPermissions(equalTo("rwxr-x---"))
+        expandDir.file('scripts/dir2/script.sh').assertPermissions(equalTo("rwxr-xr--"))
+    }
+
+    @Test public void canCreateATgzArchive() {
+        createDir('test') {
+            dir1 {
+                file 'file1.txt'
+            }
+            file 'file1.txt'
+            dir2 {
+                file 'file2.txt'
+                file 'ignored.xml'
+            }
+        }
+
+        testFile('build.gradle') << '''
+            task tar(type: Tar) {
+                compression = Compression.GZIP
+                from 'test'
+                include '**/*.txt'
+                destinationDir = buildDir
+                archiveName = 'test.tgz'
+            }
+'''
+
+        inTestDirectory().withTasks('tar').run()
+
+        TestFile expandDir = testFile('expanded')
+        testFile('build/test.tgz').untarTo(expandDir)
+        expandDir.assertHasDescendants('dir1/file1.txt', 'file1.txt', 'dir2/file2.txt')
+    }
+
+    @Test public void canCreateATbzArchive() {
+        createDir('test') {
+            dir1 {
+                file 'file1.txt'
+            }
+            file 'file1.txt'
+            dir2 {
+                file 'file2.txt'
+                file 'ignored.xml'
+            }
+        }
+
+        testFile('build.gradle') << '''
+            task tar(type: Tar) {
+                compression = Compression.BZIP2
+                from 'test'
+                include '**/*.txt'
+                destinationDir = buildDir
+                archiveName = 'test.tbz2'
+            }
+'''
+
+        inTestDirectory().withTasks('tar').run()
+
+        TestFile expandDir = testFile('expanded')
+        testFile('build/test.tbz2').untarTo(expandDir)
+        expandDir.assertHasDescendants('dir1/file1.txt', 'file1.txt', 'dir2/file2.txt')
+    }
+
+    @Test public void canCreateAJarArchiveWithDefaultManifest() {
+        createDir('test') {
+            dir1 {
+                file 'file1.txt'
+            }
+        }
+        createDir('meta-inf') {
+            file 'file1.txt'
+            dir2 {
+                file 'file2.txt'
+            }
+        }
+
+        testFile('build.gradle') << '''
+            task jar(type: Jar) {
+                from 'test'
+                metaInf {
+                    from 'meta-inf'
+                }
+                destinationDir = buildDir
+                archiveName = 'test.jar'
+            }
+'''
+
+        inTestDirectory().withTasks('jar').run()
+
+        TestFile expandDir = testFile('expanded')
+        testFile('build/test.jar').unzipTo(expandDir)
+        expandDir.assertHasDescendants('META-INF/MANIFEST.MF', 'META-INF/file1.txt', 'META-INF/dir2/file2.txt', 'dir1/file1.txt')
+
+        expandDir.file('META-INF/MANIFEST.MF').assertContents(equalTo('Manifest-Version: 1.0\r\n\r\n'))
+    }
+
+    @Test public void metaInfSpecsAreIndependentOfOtherSpec() {
+        createDir('test') {
+            dir1 {
+                file 'ignored.xml'
+                file 'file1.txt'
+            }
+        }
+        createDir('meta-inf') {
+            dir2 {
+                file 'ignored.txt'
+                file 'file2.xml'
+            }
+        }
+        createDir('meta-inf2') {
+            file 'file2.txt'
+            file 'file2.xml'
+        }
+
+        testFile('build.gradle') << '''
+            task jar(type: Jar) {
+                from 'test'
+                include '**/*.txt'
+                metaInf {
+                    from 'meta-inf'
+                    include '**/*.xml'
+                }
+                metaInf {
+                    from 'meta-inf2'
+                    into 'dir3'
+                }
+                destinationDir = buildDir
+                archiveName = 'test.jar'
+            }
+'''
+
+        inTestDirectory().withTasks('jar').run()
+
+        TestFile expandDir = testFile('expanded')
+        testFile('build/test.jar').unzipTo(expandDir)
+        expandDir.assertHasDescendants(
+                'META-INF/MANIFEST.MF',
+                'META-INF/dir2/file2.xml',
+                'META-INF/dir3/file2.txt',
+                'META-INF/dir3/file2.xml',
+                'dir1/file1.txt')
+    }
+
+    @Test public void canCreateAWarArchiveWithNoWebXml() {
+        createDir('content') {
+            content1 {
+                file 'file1.jsp'
+            }
+        }
+        createDir('web-inf') {
+            webinf1 {
+                file 'file1.txt'
+            }
+        }
+        createDir('meta-inf') {
+            metainf1 {
+                file 'file2.txt'
+            }
+        }
+        createDir('classes') {
+            org {
+                gradle {
+                    file 'resource.txt'
+                    file 'Person.class'
+                }
+            }
+        }
+        createZip("lib.jar") {
+            file "Dependency.class"
+        }
+
+        testFile('build.gradle') << '''
+            task war(type: War) {
+                from 'content'
+                metaInf {
+                    from 'meta-inf'
+                }
+                webInf {
+                    from 'web-inf'
+                }
+                classpath 'classes'
+                classpath 'lib.jar'
+                destinationDir = buildDir
+                archiveName = 'test.war'
+            }
+'''
+
+        inTestDirectory().withTasks('war').run()
+
+        TestFile expandDir = testFile('expanded')
+        testFile('build/test.war').unzipTo(expandDir)
+        expandDir.assertHasDescendants(
+                'META-INF/MANIFEST.MF',
+                'META-INF/metainf1/file2.txt',
+                'content1/file1.jsp',
+                'WEB-INF/lib/lib.jar',
+                'WEB-INF/classes/org/gradle/resource.txt',
+                'WEB-INF/classes/org/gradle/Person.class',
+                'WEB-INF/webinf1/file1.txt')
+
+        expandDir.file('META-INF/MANIFEST.MF').assertContents(equalTo('Manifest-Version: 1.0\r\n\r\n'))
+    }
+
+    @Test public void canCreateAWarArchiveWithWebXml() {
+        testFile('some.xml') << '<web/>'
+        createDir('web-inf') {
+            webinf1 {
+                file 'file1.txt'
+            }
+        }
+
+        testFile('build.gradle') << '''
+            task war(type: War) {
+                webInf {
+                    from 'web-inf'
+                    exclude '**/*.xml'
+                }
+                webXml = file('some.xml')
+                destinationDir = buildDir
+                archiveName = 'test.war'
+            }
+'''
+
+        inTestDirectory().withTasks('war').run()
+
+        TestFile expandDir = testFile('expanded')
+        testFile('build/test.war').unzipTo(expandDir)
+        expandDir.assertHasDescendants(
+                'META-INF/MANIFEST.MF',
+                'WEB-INF/web.xml',
+                'WEB-INF/webinf1/file1.txt')
+    }
+
+    @Test public void canAddFilesToWebInfDir() {
+        createDir('web-inf') {
+            webinf1 {
+                file 'file1.txt'
+                file 'ignore.xml'
+            }
+        }
+        createDir('web-inf2') {
+            file 'file2.txt'
+        }
+
+        testFile('build.gradle') << '''
+            task war(type: War) {
+                webInf {
+                    from 'web-inf'
+                    exclude '**/*.xml'
+                }
+                webInf {
+                    from 'web-inf2'
+                    into 'dir2'
+                    include '**/file2*'
+                }
+                destinationDir = buildDir
+                archiveName = 'test.war'
+            }
+'''
+
+        inTestDirectory().withTasks('war').run()
+
+        TestFile expandDir = testFile('expanded')
+        testFile('build/test.war').unzipTo(expandDir)
+        expandDir.assertHasDescendants(
+                'META-INF/MANIFEST.MF',
+                'WEB-INF/webinf1/file1.txt',
+                'WEB-INF/dir2/file2.txt')
+    }
+
+    @Test public void canCreateArchivesAndExplodedImageFromSameSpec() {
+        createDir('test') {
+            dir1 {
+                file 'file1.txt'
+                file 'ignored.xml'
+            }
+            dir2 {
+                dir3 { file 'file2.txt' }
+                file 'ignored.xml'
+            }
+        }
+
+        testFile('build.gradle') << '''
+            def distImage = copySpec {
+                include '**/*.txt'
+                from('test/dir1') {
+                    into 'lib'
+                }
+                from('test/dir2') {
+                    into 'src'
+                }
+            }
+            task copy(type: Copy) {
+                into 'build/exploded'
+                with distImage
+            }
+            task zip(type: Zip) {
+                destinationDir = file('build')
+                archiveName = 'test.zip'
+                into 'prefix'
+                with distImage
+            }
+'''
+
+        inTestDirectory().withTasks('copy', 'zip').run()
+        testFile('build/exploded').assertHasDescendants(
+                'lib/file1.txt', 'src/dir3/file2.txt'
+        )
+        TestFile expandDir = testFile('expanded')
+        testFile('build/test.zip').unzipTo(expandDir)
+        expandDir.assertHasDescendants('prefix/lib/file1.txt', 'prefix/src/dir3/file2.txt')
+    }
+
+    @Test public void canCreateExplodedImageFromArchiveTask() {
+        createDir('test') {
+            dir1 {
+                file 'file1.txt'
+                file 'ignored.xml'
+            }
+            dir2 {
+                dir3 { file 'file2.txt' }
+                file 'ignored.xml'
+            }
+        }
+
+        testFile('build.gradle') << '''
+            task zip(type: Zip) {
+                destinationDir = file('build')
+                archiveName = 'test.zip'
+                into 'prefix'
+                from 'test'
+                include '**/*.txt'
+            }
+            task explodedZip(type: Copy) {
+                into 'build/exploded'
+                with zip
+            }
+            task copyFromRootSpec(type: Copy) {
+                into 'build/copy'
+                with zip.rootSpec
+            }
+'''
+
+        inTestDirectory().withTasks('explodedZip', 'copyFromRootSpec').run()
+        testFile('build/exploded').assertHasDescendants(
+                'prefix/dir1/file1.txt', 'prefix/dir2/dir3/file2.txt'
+        )
+        testFile('build/copy').assertHasDescendants(
+                'prefix/dir1/file1.txt', 'prefix/dir2/dir3/file2.txt'
+        )
+    }
+
+    @Test public void canMergeArchivesIntoAnotherZip() {
+        createZip('test.zip') {
+            shared {
+                file 'zip.txt'
+            }
+            zipdir1 {
+                file 'file1.txt'
+            }
+        }
+        createTar('test.tar') {
+            shared {
+                file 'tar.txt'
+            }
+            tardir1 {
+                file 'file1.txt'
+            }
+        }
+        createDir('test') {
+            shared {
+                file 'dir.txt'
+            }
+            dir1 {
+                file 'file1.txt'
+            }
+        }
+
+        testFile('build.gradle') << '''
+        task zip(type: Zip) {
+            from zipTree('test.zip')
+            from tarTree('test.tar')
+            from fileTree('test')
+            destinationDir = buildDir
+            archiveName = 'test.zip'
+        }
+        '''
+
+        inTestDirectory().withTasks('zip').run()
+
+        TestFile expandDir = testFile('expanded')
+        testFile('build/test.zip').unzipTo(expandDir)
+        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)
+        zipRoot.create(cl)
+        zipRoot.zipTo(zip)
+    }
+
+    private def createTar(String name, Closure cl) {
+        TestFile tarRoot = testFile("${name}.root")
+        TestFile tar = testFile(name)
+        tarRoot.create(cl)
+        tarRoot.tarTo(tar)
+    }
+
+    private def createDir(String name, Closure cl) {
+        TestFile root = testFile(name)
+        root.create(cl)
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ArtifactDependenciesIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ArtifactDependenciesIntegrationTest.groovy
new file mode 100644
index 0000000..a45b682
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ArtifactDependenciesIntegrationTest.groovy
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.integtests.fixtures.internal.AbstractIntegrationTest
+import org.gradle.util.TestFile
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.gradle.integtests.fixtures.*
+import static org.hamcrest.Matchers.containsString
+import static org.hamcrest.Matchers.startsWith
+
+class ArtifactDependenciesIntegrationTest extends AbstractIntegrationTest {
+    @Rule
+    public final TestResources testResources = new TestResources()
+    @Rule
+    public final HttpServer server = new HttpServer()
+
+    @Before
+    public void setup() {
+        distribution.requireOwnUserHomeDir()
+    }
+
+    @Test
+    public void canResolveDependenciesFromAFlatDir() {
+        File buildFile = testFile("projectWithFlatDir.gradle");
+        usingBuildFile(buildFile).run();
+    }
+
+    @Test
+    public void canHaveConfigurationHierarchy() {
+        File buildFile = testFile("projectWithConfigurationHierarchy.gradle");
+        usingBuildFile(buildFile).run();
+    }
+
+    @Test
+    public void dependencyReportWithConflicts() {
+        File buildFile = testFile("projectWithConflicts.gradle");
+        usingBuildFile(buildFile).run();
+        usingBuildFile(buildFile).withDependencyList().run();
+    }
+
+    @Test
+    public void canNestModules() throws IOException {
+        File buildFile = testFile("projectWithNestedModules.gradle");
+        usingBuildFile(buildFile).run();
+    }
+
+    @Test
+    public void canHaveCycleInDependencyGraph() throws IOException {
+        File buildFile = testFile("projectWithCyclesInDependencyGraph.gradle");
+        usingBuildFile(buildFile).run();
+    }
+
+    @Test
+    public void canUseDynamicVersions() throws IOException {
+        File buildFile = testFile("projectWithDynamicVersions.gradle");
+        usingBuildFile(buildFile).run();
+    }
+
+    @Test
+    public void reportsUnknownDependencyError() {
+        File buildFile = testFile("projectWithUnknownDependency.gradle");
+        ExecutionFailure failure = usingBuildFile(buildFile).runWithFailure();
+        failure.assertHasFileName("Build file '" + buildFile.getPath() + "'");
+        failure.assertHasDescription("Execution failed for task ':listJars'");
+        failure.assertThatCause(startsWith("Could not resolve all dependencies for configuration ':compile'"));
+        failure.assertThatCause(containsString("unresolved dependency: test#unknownProjectA;1.2: not found"));
+        failure.assertThatCause(containsString("unresolved dependency: test#unknownProjectB;2.1.5: not found"));
+    }
+
+    @Test
+    public void reportsProjectDependsOnSelfError() {
+        TestFile buildFile = testFile("build.gradle");
+        buildFile << '''
+            configurations { compile }
+            dependencies { compile project(':') }
+            defaultTasks 'listJars'
+            task listJars << { configurations.compile.each { println it } }
+'''
+        ExecutionFailure failure = usingBuildFile(buildFile).runWithFailure();
+        failure.assertHasFileName("Build file '" + buildFile.getPath() + "'");
+        failure.assertHasDescription("Execution failed for task ':listJars'");
+        failure.assertThatCause(startsWith("Could not resolve all dependencies for configuration ':compile'"));
+        failure.assertThatCause(containsString("a module is not authorized to depend on itself"));
+    }
+
+    @Test
+    public void canSpecifyProducerTasksForFileDependency() {
+        testFile("settings.gradle").write("include 'sub'");
+        testFile("build.gradle") << '''
+            configurations { compile }
+            dependencies { compile project(path: ':sub', configuration: 'compile') }
+            task test(dependsOn: configurations.compile) << {
+                assert file('sub/sub.jar').isFile()
+            }
+'''
+        testFile("sub/build.gradle") << '''
+            configurations { compile }
+            dependencies { compile files('sub.jar') { builtBy 'jar' } }
+            task jar << { file('sub.jar').text = 'content' }
+'''
+
+        inTestDirectory().withTasks("test").run().assertTasksExecuted(":sub:jar", ":test");
+    }
+
+    @Test
+    public void resolvedProjectArtifactsContainProjectVersionInTheirNames() {
+        testFile('settings.gradle').write("include 'a', 'b'");
+        testFile('a/build.gradle') << '''
+            apply plugin: 'base'
+            configurations { compile }
+            task aJar(type: Jar) { }
+            artifacts { compile aJar }
+'''
+        testFile('b/build.gradle') << '''
+            apply plugin: 'base'
+            version = 'early'
+            configurations { compile }
+            task bJar(type: Jar) { }
+            gradle.taskGraph.whenReady { project.version = 'late' }
+            artifacts { compile bJar }
+'''
+        testFile('build.gradle') << '''
+            configurations { compile }
+            dependencies { compile project(path: ':a', configuration: 'compile'), project(path: ':b', configuration: 'compile') }
+            task test(dependsOn: configurations.compile) << {
+                assert configurations.compile.collect { it.name } == ['a.jar', 'b-late.jar']
+            }
+'''
+        inTestDirectory().withTasks('test').run()
+    }
+
+    @Test
+    public void canUseArtifactSelectorForProjectDependencies() {
+        testFile('settings.gradle').write("include 'a', 'b'");
+        testFile('a/build.gradle') << '''
+            apply plugin: 'base'
+            configurations { 'default' {} }
+            task aJar(type: Jar) { }
+            artifacts { 'default' aJar }
+'''
+        testFile('b/build.gradle') << '''
+            configurations { compile }
+            dependencies { compile(project(':a')) { artifact { name = 'a'; type = 'jar' } } }
+            task test {
+                inputs.files configurations.compile
+                doFirst {
+                    assert [project(':a').tasks.aJar.archivePath] as Set == configurations.compile.files
+                }
+            }
+'''
+        inTestDirectory().withTasks('test').run()
+    }
+
+    @Test
+    public void canHaveCycleInProjectDependencies() {
+        inTestDirectory().withTasks('listJars').run();
+    }
+
+    @Test
+    public void canHaveNonTransitiveProjectDependencies() {
+        repo().module("group", "externalA", 1.5).publishArtifact()
+
+        testFile('settings.gradle') << "include 'a', 'b'"
+
+        testFile('build.gradle') << '''
+allprojects {
+    apply plugin: 'java'
+    repositories { mavenRepo urls: rootProject.uri('repo') }
+}
+project(':a') {
+    dependencies {
+        compile 'group:externalA:1.5'
+        compile files('libs/externalB.jar')
+    }
+}
+project(':b') {
+    configurations.compile.transitive = false
+    dependencies {
+        compile project(':a') { transitive = false }
+    }
+    task listJars << {
+        assert configurations.compile.collect { it.name } == ['a.jar']
+    }
+}
+'''
+
+        inTestDirectory().withTasks('listJars').run()
+    }
+
+    @Test
+    public void canResolveAndCacheDependenciesFromHttpIvyRepository() {
+        def repo = ivyRepo()
+        def module = repo.module('group', 'projectA', '1.2')
+        module.publishArtifact()
+
+        server.expectGet('/repo/group/projectA/1.2/ivy-1.2.xml', module.ivyFile)
+        server.expectGet('/repo/group/projectA/1.2/projectA-1.2.jar', module.jarFile)
+        server.start()
+
+        testFile("build.gradle") << """
+apply plugin: 'java'
+repositories {
+    ivy {
+        name = 'gradleReleases'
+        artifactPattern "http://localhost:${server.port}/repo/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]"
+    }
+}
+dependencies {
+    compile 'group:projectA:1.2'
+}
+task listJars << {
+    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
+}
+"""
+
+        inTestDirectory().withTasks('listJars').run()
+        inTestDirectory().withTasks('listJars').run()
+    }
+    
+    @Test
+    public void reportsMissingAndFailedHttpDownload() {
+        server.start()
+
+        testFile("build.gradle") << """
+apply plugin: 'java'
+repositories {
+    ivy {
+        name = 'gradleReleases'
+        artifactPattern "http://localhost:${server.port}/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]"
+    }
+}
+dependencies {
+    compile 'group:org:1.2'
+}
+task show << { println configurations.compile.files }
+"""
+
+        def result = executer.withTasks("show").runWithFailure()
+        result.assertHasDescription('Execution failed for task \':show\'.')
+        result.assertHasCause('Could not resolve all dependencies for configuration \':compile\':')
+        assert result.getOutput().contains('group#org;1.2: not found')
+
+        server.addBroken('/')
+
+        result = executer.withTasks("show").runWithFailure()
+        result.assertHasDescription('Execution failed for task \':show\'.')
+        result.assertHasCause('Could not resolve all dependencies for configuration \':compile\':')
+    }
+
+    MavenRepository repo() {
+        return new MavenRepository(testFile('repo'))
+    }
+
+    IvyRepository ivyRepo() {
+        return new IvyRepository(testFile('ivy-repo'))
+    }
+}
+
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/BuildAggregationIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/BuildAggregationIntegrationTest.groovy
new file mode 100644
index 0000000..f762d06
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/BuildAggregationIntegrationTest.groovy
@@ -0,0 +1,95 @@
+/*
+ * 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.integtests.fixtures.ExecutionFailure
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.gradle.util.TestFile
+import org.junit.Rule
+import org.junit.Test
+import static org.hamcrest.Matchers.*
+
+class BuildAggregationIntegrationTest {
+    @Rule public final GradleDistribution dist = new GradleDistribution()
+    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+
+    @Test
+    public void canExecuteAnotherBuildFromBuild() {
+        dist.testFile('build.gradle') << '''
+            assert gradle.parent == null
+            task build(type: GradleBuild) {
+                dir = 'other'
+                tasks = ['dostuff']
+                startParameter.searchUpwards = false
+            }
+'''
+
+        dist.testFile('other/build.gradle') << '''
+            assert gradle.parent != null
+            task dostuff << {
+                assert gradle.parent != null
+            }
+'''
+
+        executer.withTasks('build').run()
+    }
+
+    @Test
+    public void treatsBuildSrcProjectAsANestedBuild() {
+        dist.testFile('build.gradle') << '''
+            assert gradle.parent == null
+            task build
+'''
+
+        dist.testFile('buildSrc/build.gradle') << '''
+            apply plugin: 'java'
+            assert gradle.parent != null
+            classes << {
+                assert gradle.parent != null
+            }
+'''
+
+        executer.withTasks('build').run()
+    }
+
+    @Test
+    public void reportsNestedBuildFailure() {
+        TestFile other = dist.testFile('other.gradle') << '''
+            1/0
+'''
+
+        dist.testFile('build.gradle') << '''
+            task build(type: GradleBuild) {
+                buildFile = 'other.gradle'
+                startParameter.searchUpwards = false
+            }
+'''
+
+        ExecutionFailure failure = executer.withTasks('build').runWithFailure()
+        failure.assertHasFileName("Build file '${other}'")
+        failure.assertHasLineNumber(2)
+        failure.assertHasDescription('A problem occurred evaluating root project')
+        failure.assertThatCause(containsString('Division by zero'))
+    }
+
+    @Test
+    public void reportsBuildSrcFailure() {
+        dist.testFile('buildSrc/src/main/java/Broken.java') << 'broken!'
+        ExecutionFailure failure = executer.runWithFailure()
+        failure.assertHasDescription('Execution failed for task \':compileJava\'')
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/BuildScriptClasspathIntegrationTest.java b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/BuildScriptClasspathIntegrationTest.java
new file mode 100644
index 0000000..f9faa13
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/BuildScriptClasspathIntegrationTest.java
@@ -0,0 +1,185 @@
+/*
+ * 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.integtests.fixtures.ArtifactBuilder;
+import org.gradle.integtests.fixtures.ExecutionFailure;
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class BuildScriptClasspathIntegrationTest extends AbstractIntegrationTest {
+    @Test
+    public void providesADefaultBuildForBuildSrcProject() {
+        testFile("buildSrc/src/main/java/BuildClass.java").writelns("public class BuildClass { }");
+        testFile("build.gradle").writelns("new BuildClass()");
+        inTestDirectory().withTaskList().run();
+    }
+
+    @Test
+    public void buildSrcProjectCanReferToSourceOutsideBuildSrcDir() {
+        testFile("gradle/src/BuildClass.java").writelns("public class BuildClass { }");
+        testFile("buildSrc/build.gradle").writelns(
+                "apply plugin: 'java'",
+                "sourceSets.main.java.srcDirs = ['../gradle/src']"
+        );
+        testFile("build.gradle").writelns(
+                "task test << { new BuildClass() }"
+        );
+
+        inTestDirectory().withTasks("test").run();
+
+        testFile("gradle/src/BuildClass.java").writelns("public class BuildClass { public BuildClass(String value) { throw new RuntimeException(\"broken\"); } }");
+
+        ExecutionFailure failure = inTestDirectory().withTasks("test").runWithFailure();
+        failure.assertHasCause("broken");
+    }
+
+    @Test
+    public void canDeclareClasspathInBuildScript() {
+        ArtifactBuilder builder = artifactBuilder();
+        builder.sourceFile("org/gradle/test/ImportedClass.java").writelns(
+                "package org.gradle.test;",
+                "public class ImportedClass { }"
+        );
+        builder.sourceFile("org/gradle/test/StaticImportedClass.java").writelns(
+                "package org.gradle.test;",
+                "public class StaticImportedClass { public static int someValue = 12; }"
+        );
+        builder.sourceFile("org/gradle/test/StaticImportedFieldClass.java").writelns(
+                "package org.gradle.test;",
+                "public class StaticImportedFieldClass { public static int anotherValue = 4; }"
+        );
+        builder.sourceFile("org/gradle/test2/OnDemandImportedClass.java").writelns(
+                "package org.gradle.test2;",
+                "public class OnDemandImportedClass { }"
+        );
+        builder.buildJar(testFile("repo/test-1.3.jar"));
+
+        testFile("build.gradle").writelns(
+                "import org.gradle.test.ImportedClass",
+                "import static org.gradle.test.StaticImportedClass.*",
+                "import static org.gradle.test.StaticImportedFieldClass.anotherValue",
+                "import org.gradle.test2.*",
+                "buildscript {",
+                "  repositories {",
+                "    flatDir dirs: file('repo')",
+                "  }",
+                "  dependencies {",
+                "    classpath name: 'test', version: '1.+'",
+                "  }",
+                "}",
+                "task hello << {",
+                "  new org.gradle.test.ImportedClass()",
+                "  println someValue",
+                "  println anotherValue",
+                "  new ImportedClass()",
+                "  new OnDemandImportedClass()",
+                "}",
+                "a = new ImportedClass()",
+                "b = OnDemandImportedClass",
+                "c = someValue",
+                "d = anotherValue",
+                "class TestClass extends ImportedClass { }",
+                "def aMethod() { return new OnDemandImportedClass() }"
+        );
+        inTestDirectory().withTasks("hello").run();
+    }
+
+    @Test
+    public void canUseBuildSrcAndSystemClassesInClasspathDeclaration() {
+        testFile("buildSrc/src/main/java/org/gradle/buildsrc/test/ImportedClass.java").writelns(
+                "package org.gradle.buildsrc.test;",
+                "public class ImportedClass { }"
+        );
+        testFile("buildSrc/src/main/java/org/gradle/buildsrc/test/StaticImportedClass.java").writelns(
+                "package org.gradle.buildsrc.test;",
+                "public class StaticImportedClass { public static int someValue = 12; }"
+        );
+        testFile("buildSrc/src/main/java/org/gradle/buildsrc/test/StaticImportedFieldClass.java").writelns(
+                "package org.gradle.buildsrc.test;",
+                "public class StaticImportedFieldClass { public static int anotherValue = 4; }"
+        );
+        testFile("buildSrc/src/main/java/org/gradle/buildsrc/test2/OnDemandImportedClass.java").writelns(
+                "package org.gradle.buildsrc.test2;",
+                "public class OnDemandImportedClass { }"
+        );
+
+        testFile("build.gradle").writelns(
+                "import org.gradle.buildsrc.test.ImportedClass",
+                "import org.gradle.buildsrc.test2.*",
+                "import static org.gradle.buildsrc.test.StaticImportedClass.*",
+                "import static org.gradle.buildsrc.test.StaticImportedFieldClass.anotherValue",
+                "buildscript {",
+                "    new ImportedClass()",
+                "    new org.gradle.buildsrc.test.ImportedClass()",
+                "    new org.gradle.buildsrc.test2.OnDemandImportedClass()",
+                "    println someValue",
+                "    println anotherValue",
+                "    List l = new ArrayList()",
+                "    Project p = project",
+                "    Closure cl = { }",
+                "}",
+                "task hello"
+        );
+        inTestDirectory().withTasks("hello").run();
+    }
+
+    @Test
+    public void inheritsClassPathOfParentProject() {
+        ArtifactBuilder builder = artifactBuilder();
+        builder.sourceFile("org/gradle/test/BuildClass.java").writelns(
+                "package org.gradle.test;",
+                "public class BuildClass { }"
+        );
+        builder.buildJar(testFile("repo/test-1.3.jar"));
+        testFile("settings.gradle").writelns(
+                "include 'child'"
+        );
+        testFile("build.gradle").writelns(
+                "assert gradle.scriptClassLoader == buildscript.classLoader.parent",
+                "buildscript {",
+                "    repositories { flatDir(dirs: file('repo')) }",
+                "    dependencies { classpath name: 'test', version: '1.3' }",
+                "}"
+        );
+        testFile("child/build.gradle").writelns(
+                "assert parent.buildscript.classLoader == buildscript.classLoader.parent",
+                "task hello << ",
+                "{",
+                "    new org.gradle.test.BuildClass()",
+                "}"
+        );
+        inTestDirectory().withTasks("hello").run();
+    }
+
+    @Test @Ignore
+    public void reportsFailureDuringClasspathDeclaration() {
+        fail("implement me");
+    }
+
+    @Test @Ignore
+    public void canInjectClassPathIntoSubProjects() {
+        fail("implement me");
+    }
+    
+    @Test @Ignore
+    public void canReuseClassPathRepositories() {
+        fail("implement me");
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/BuildScriptErrorIntegrationTest.java b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/BuildScriptErrorIntegrationTest.java
new file mode 100644
index 0000000..c329c72
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/BuildScriptErrorIntegrationTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.integtests.fixtures.ExecutionFailure;
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest;
+import org.gradle.util.TestFile;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.gradle.util.Matchers.containsLine;
+
+public class BuildScriptErrorIntegrationTest extends AbstractIntegrationTest {
+    @Test
+    public void reportsProjectEvaluationFailsWithGroovyException() {
+        TestFile buildFile = testFile("build.gradle");
+        buildFile.writelns("", "createTakk('do-stuff')");
+        ExecutionFailure failure = usingBuildFile(buildFile).runWithFailure();
+
+        failure.assertHasFileName(String.format("Build file '%s'", buildFile));
+        failure.assertHasLineNumber(2);
+        failure.assertHasDescription("A problem occurred evaluating root project 'reportsProjectEvaluationFailsWithGroovyException");
+        failure.assertHasCause("Could not find method createTakk() for arguments [do-stuff] on root project 'reportsProjectEvaluationFailsWithGroovyException");
+    }
+
+    @Test
+    public void reportsScriptCompilationException() {
+        TestFile buildFile = testFile("build.gradle");
+        buildFile.writelns(
+            "// a comment",
+            "import org.gradle.unknown.Unknown",
+            "new Unknown()");
+        ExecutionFailure failure = inTestDirectory().runWithFailure();
+        failure.assertHasFileName(String.format("Build file '%s'", buildFile));
+        failure.assertHasLineNumber(2);
+        failure.assertHasDescription(String.format("Could not compile build file '%s'.", buildFile));
+        failure.assertThatCause(containsLine(String.format("build file '%s': 2: unable to resolve class org.gradle.unknown.Unknown", buildFile)));
+    }
+
+    @Test
+    public void reportsNestedProjectEvaluationFailsWithRuntimeException() {
+        testFile("settings.gradle").write("include 'child'");
+
+        TestFile buildFile = testFile("build.gradle");
+        buildFile.writelns(
+                "dependsOn 'child'",
+                "task t");
+
+        TestFile childBuildFile = testFile("child/build.gradle");
+        childBuildFile.writelns(
+                "def broken = { ->",
+                "    throw new RuntimeException('failure') }",
+                "broken()");
+        ExecutionFailure failure = inTestDirectory().withTasks("t").runWithFailure();
+
+        failure.assertHasFileName(String.format("Build file '%s'", childBuildFile));
+        failure.assertHasLineNumber(2);
+        failure.assertHasDescription("A problem occurred evaluating project ':child'");
+        failure.assertHasCause("failure");
+    }
+
+    @Test
+    public void reportsTaskGraphReadyEventFailsWithRuntimeException() {
+        TestFile buildFile = testFile("build.gradle");
+        buildFile.writelns(
+                "gradle.taskGraph.whenReady {",
+                "throw new RuntimeException('broken closure')",
+                "}",
+                "task a");
+
+        ExecutionFailure failure = usingBuildFile(buildFile).withTasks("a").runWithFailure();
+
+        failure.assertHasFileName(String.format("Build file '%s'", buildFile));
+        failure.assertHasLineNumber(2);
+        failure.assertHasDescription("broken closure");
+        failure.assertHasNoCause();
+    }
+
+    @Test @Ignore
+    public void reportsTaskDependencyClosureFailsWithRuntimeException() {
+        TestFile buildFile = testFile("build.gradle");
+        buildFile.writelns(
+                "task a",
+                "a.dependsOn {",
+                "throw new RuntimeException('broken')",
+                "}");
+
+        ExecutionFailure failure = usingBuildFile(buildFile).withTasks("a").runWithFailure();
+
+        failure.assertHasFileName(String.format("Build file '%s'", buildFile));
+        failure.assertHasLineNumber(3);
+        failure.assertHasDescription("??");
+        failure.assertHasCause("broken");
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/BuildScriptExecutionIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/BuildScriptExecutionIntegrationTest.groovy
new file mode 100644
index 0000000..4f03697
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/BuildScriptExecutionIntegrationTest.groovy
@@ -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.integtests
+
+import org.gradle.integtests.fixtures.ExecutionResult
+import org.gradle.util.TestFile
+import org.junit.Test
+import static org.hamcrest.Matchers.*
+import static org.junit.Assert.*
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest
+
+class BuildScriptExecutionIntegrationTest extends AbstractIntegrationTest {
+
+    @Test
+    public void executesBuildScriptWithCorrectEnvironment() {
+        TestFile buildScript = testFile('build.gradle')
+        buildScript << """
+            println 'quiet message'
+            captureStandardOutput(LogLevel.ERROR)
+            println 'error message'
+            assert project != null
+            assert "${buildScript.absolutePath.replace("\\", "\\\\")}" == buildscript.sourceFile as String
+            assert "${buildScript.toURI()}" == buildscript.sourceURI as String
+            assert buildscript.classLoader == getClass().classLoader.parent
+            assert buildscript.classLoader == Thread.currentThread().contextClassLoader
+            assert gradle.scriptClassLoader == buildscript.classLoader.parent
+
+            task doStuff
+"""
+
+        ExecutionResult result = inTestDirectory().withTasks('doStuff').run()
+        assertThat(result.output, containsString('quiet message'))
+        assertThat(result.output, not(containsString('error message')))
+        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()
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CacheProjectIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CacheProjectIntegrationTest.groovy
new file mode 100644
index 0000000..ae7f57f
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CacheProjectIntegrationTest.groovy
@@ -0,0 +1,123 @@
+/*
+ * 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.groovy.scripts.ScriptSource
+import org.gradle.groovy.scripts.UriScriptSource
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.gradle.util.GradleVersion
+import org.gradle.util.TestFile
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import static org.junit.Assert.*
+
+/**
+ * @author Hans Dockter
+ */
+class CacheProjectIntegrationTest {
+    static final String TEST_FILE = "build/test.txt"
+
+    @Rule public final GradleDistribution dist = new GradleDistribution()
+    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+
+    TestFile projectDir
+    TestFile userHomeDir
+    TestFile buildFile
+    TestFile propertiesFile
+    TestFile classFile
+    TestFile artifactsCache
+
+    @Before
+    public void setUp() {
+        String version = GradleVersion.current().version
+        projectDir = dist.getTestDir().file("project")
+        projectDir.mkdirs()
+        userHomeDir = dist.getUserHomeDir()
+        buildFile = projectDir.file('build.gradle')
+        ScriptSource source = new UriScriptSource("build file", buildFile)
+        propertiesFile = userHomeDir.file("caches/$version/scripts/$source.className/cache.properties")
+        classFile = userHomeDir.file("caches/$version/scripts/$source.className/no_buildscript_ProjectScript/${source.className}.class")
+        artifactsCache = projectDir.file(".gradle/$version/taskArtifacts/cache.bin")
+    }
+
+    @Test
+    public void cachesBuildScript() {
+        createLargeBuildScript()
+        testBuild("hello1", "Hello 1")
+        TestFile.Snapshot classFileSnapshot = classFile.snapshot()
+        TestFile.Snapshot artifactsCacheSnapshot = artifactsCache.snapshot()
+
+        testBuild("hello2", "Hello 2")
+        classFile.assertHasNotChangedSince(classFileSnapshot)
+        artifactsCache.assertHasNotChangedSince(artifactsCacheSnapshot)
+
+        modifyLargeBuildScript()
+        testBuild("newTask", "I am new")
+        classFile.assertHasChangedSince(classFileSnapshot)
+        artifactsCache.assertHasNotChangedSince(artifactsCacheSnapshot)
+        classFileSnapshot = classFile.snapshot()
+        artifactsCacheSnapshot = artifactsCache.snapshot()
+
+        testBuild("newTask", "I am new", "-Crebuild")
+        classFile.assertHasChangedSince(classFileSnapshot)
+        artifactsCache.assertHasChangedSince(artifactsCacheSnapshot)
+    }
+
+    private def testBuild(String taskName, String expected, String... args) {
+        executer.inDirectory(projectDir).withTasks(taskName).withArguments(args).withQuietLogging().run()
+        assertEquals(expected, projectDir.file(TEST_FILE).text)
+        classFile.assertIsFile()
+        propertiesFile.assertIsFile()
+        artifactsCache.assertIsFile()
+    }
+
+    // We once ran into a cache problem under windows, which was not reproducible with small build scripts. Therefore we
+    // create a larger one here.
+
+    def createLargeBuildScript() {
+        File buildFile = projectDir.file('build.gradle')
+        String content = ""
+        50.times {i ->
+            content += """task 'hello$i' << {
+    File file = file('$TEST_FILE')
+    file.parentFile.mkdirs()
+    file.write('Hello $i')
+}
+
+void someMethod$i() {
+    println('Some message')
+}
+
+"""
+        }
+        buildFile.write(content)
+    }
+
+    def void modifyLargeBuildScript() {
+        File buildFile = projectDir.file('build.gradle')
+        String newContent = buildFile.text + """
+task newTask << {
+    File file = file('$TEST_FILE')
+    file.parentFile.mkdirs()
+    file.write('I am new')
+}
+"""
+        buildFile.write(newContent)
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/ClientModuleDependenciesResolveIntegrationTest.java b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ClientModuleDependenciesResolveIntegrationTest.java
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/ClientModuleDependenciesResolveIntegrationTest.java
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ClientModuleDependenciesResolveIntegrationTest.java
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CodeQualityIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CodeQualityIntegrationTest.groovy
new file mode 100644
index 0000000..801858d
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CodeQualityIntegrationTest.groovy
@@ -0,0 +1,188 @@
+/*
+ * 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.integtests.fixtures.ExecutionFailure
+import org.gradle.util.TestFile
+import org.hamcrest.Matcher
+import org.junit.Test
+import static org.gradle.util.Matchers.*
+import static org.hamcrest.Matchers.*
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest
+
+class CodeQualityIntegrationTest extends AbstractIntegrationTest {
+    @Test
+    public void handlesEmptyProjects() {
+        testFile('build.gradle') << '''
+apply plugin: 'groovy'
+apply plugin: 'code-quality'
+'''
+        inTestDirectory().withTasks('check').run()
+    }
+
+    @Test
+    public void generatesReportForJavaSource() {
+        testFile('build.gradle') << '''
+apply plugin: 'java'
+apply plugin: 'code-quality'
+'''
+        writeCheckstyleConfig()
+
+        testFile('src/main/java/org/gradle/Class1.java') << 'package org.gradle; class Class1 { }'
+        testFile('src/test/java/org/gradle/TestClass1.java') << 'package org.gradle; class TestClass1 { }'
+
+        inTestDirectory().withTasks('check').run()
+
+        testFile('build/checkstyle/main.xml').assertContents(containsClass('org.gradle.Class1'))
+        testFile('build/checkstyle/test.xml').assertContents(containsClass('org.gradle.TestClass1'))
+    }
+
+    @Test
+    public void generatesReportForJavaSourceInGroovySourceDirs() {
+        testFile('build.gradle') << '''
+apply plugin: 'groovy'
+apply plugin: 'code-quality'
+dependencies { groovy localGroovy() }
+'''
+        writeCheckstyleConfig()
+
+        testFile('src/main/groovy/org/gradle/Class1.java') << 'package org.gradle; class Class1 { }'
+        testFile('src/test/groovy/org/gradle/TestClass1.java') << 'package org.gradle; class TestClass1 { }'
+
+        inTestDirectory().withTasks('check').run()
+
+        testFile('build/checkstyle/main.xml').assertContents(containsClass('org.gradle.Class1'))
+        testFile('build/checkstyle/test.xml').assertContents(containsClass('org.gradle.TestClass1'))
+    }
+
+    private Matcher<String> containsClass(String classname) {
+        return containsLine(containsString(classname.replace('.', File.separator) + '.java'))
+    }
+
+    @Test
+    public void checkstyleOnlyChecksJavaSource() {
+        testFile('build.gradle') << '''
+apply plugin: 'groovy'
+apply plugin: 'code-quality'
+'''
+        writeCheckstyleConfig()
+
+        testFile('src/main/groovy/org/gradle/Class1.java') << 'package org.gradle; class Class1 { }'
+        testFile('src/main/groovy/org/gradle/Class2.java') << 'package org.gradle; class Class2 { }'
+        testFile('src/main/groovy/org/gradle/class3.groovy') << 'package org.gradle; class class3 { }'
+
+        inTestDirectory().withTasks('checkstyleMain').run()
+
+        testFile('build/checkstyle/main.xml').assertExists()
+        testFile('build/checkstyle/main.xml').assertContents(not(containsClass('org.gradle.class3')))
+    }
+
+    @Test
+    public void checkstyleViolationBreaksBuild() {
+        testFile('build.gradle') << '''
+apply plugin: 'groovy'
+apply plugin: 'code-quality'
+'''
+        writeCheckstyleConfig()
+
+        testFile('src/main/java/org/gradle/class1.java') << 'package org.gradle; class class1 { }'
+        testFile('src/main/groovy/org/gradle/class2.java') << 'package org.gradle; class class2 { }'
+
+        ExecutionFailure failure = inTestDirectory().withTasks('check').runWithFailure()
+        failure.assertHasDescription('Execution failed for task \':checkstyleMain\'')
+        failure.assertThatCause(startsWith('Checkstyle check violations were found in main Java source. See the report at'))
+
+        testFile('build/checkstyle/main.xml').assertExists()
+    }
+
+    @Test
+    public void generatesReportForGroovySource() {
+        testFile('build.gradle') << '''
+apply plugin: 'groovy'
+apply plugin: 'code-quality'
+dependencies { groovy localGroovy() }
+'''
+        writeCodeNarcConfigFile()
+
+        testFile('src/main/groovy/org/gradle/Class1.groovy') << 'package org.gradle; class Class1 { }'
+        testFile('src/test/groovy/org/gradle/TestClass1.groovy') << 'package org.gradle; class TestClass1 { }'
+
+        inTestDirectory().withTasks('check').run()
+
+        testFile('build/reports/codenarc/main.html').assertExists()
+        testFile('build/reports/codenarc/test.html').assertExists()
+    }
+
+    @Test
+    public void codeNarcOnlyChecksGroovySource() {
+        testFile('build.gradle') << '''
+apply plugin: 'groovy'
+apply plugin: 'code-quality'
+'''
+
+        writeCodeNarcConfigFile()
+
+        testFile('src/main/groovy/org/gradle/class1.java') << 'package org.gradle; class class1 { }'
+        testFile('src/main/groovy/org/gradle/Class2.groovy') << 'package org.gradle; class Class2 { }'
+
+        inTestDirectory().withTasks('codenarcMain').run()
+
+        testFile('build/reports/codenarc/main.html').assertExists()
+    }
+
+    @Test
+    public void codeNarcViolationBreaksBuild() {
+        testFile('build.gradle') << '''
+apply plugin: 'groovy'
+apply plugin: 'code-quality'
+dependencies { groovy localGroovy() }
+'''
+
+        writeCodeNarcConfigFile()
+
+        testFile('src/main/groovy/org/gradle/class1.groovy') << 'package org.gradle; class class1 { }'
+
+        ExecutionFailure failure = inTestDirectory().withTasks('check').runWithFailure()
+        failure.assertHasDescription('Execution failed for task \':codenarcMain\'')
+        failure.assertThatCause(startsWith('CodeNarc check violations were found in main Groovy source. See the report at '))
+
+        testFile('build/reports/codenarc/main.html').assertExists()
+    }
+
+    private TestFile writeCheckstyleConfig() {
+        return testFile('config/checkstyle/checkstyle.xml') << '''
+<!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="TreeWalker">
+        <module name="TypeName"/>
+    </module>
+</module>'''
+    }
+
+    private TestFile writeCodeNarcConfigFile() {
+        return testFile('config/codenarc/codenarc.xml') << '''
+<ruleset xmlns="http://codenarc.org/ruleset/1.0"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://codenarc.org/ruleset/1.0 http://codenarc.org/ruleset-schema.xsd"
+        xsi:noNamespaceSchemaLocation="http://codenarc.org/ruleset-schema.xsd">
+    <ruleset-ref path='rulesets/naming.xml'/>
+</ruleset>
+'''
+    }
+
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CommandLineIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CommandLineIntegrationTest.groovy
new file mode 100644
index 0000000..aff2b33
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CommandLineIntegrationTest.groovy
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.integtests.fixtures.ExecutionFailure
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.util.Jvm
+import org.gradle.util.OperatingSystem
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+public class CommandLineIntegrationTest {
+    @Rule public final GradleDistribution dist = new GradleDistribution()
+    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+    @Rule public final TestResources resources = new TestResources()
+
+    @Test
+    public void hasNonZeroExitCodeOnBuildFailure() {
+        ExecutionFailure failure = executer.withTasks('unknown').runWithFailure()
+        failure.assertHasDescription("Task 'unknown' not found in root project 'commandLine'.")
+    }
+
+    @Test
+    public void canonicalisesWorkingDirectory() {
+        File javaprojectDir;
+        if (OperatingSystem.current().isWindows()) {
+            javaprojectDir = new File(dist.samplesDir, 'java/QUICKS~1')
+        } else if (!OperatingSystem.current().isCaseSensitiveFileSystem()) {
+            javaprojectDir = new File(dist.samplesDir, 'JAVA/QuickStart')
+        } else {
+            javaprojectDir = new File(dist.samplesDir, 'java/multiproject/../quickstart')
+        }
+        executer.inDirectory(javaprojectDir).withTasks('classes').run()
+    }
+
+    @Test
+    public void canDefineJavaHomeViaEnvironmentVariable() {
+        String javaHome = Jvm.current().javaHome
+        String expectedJavaHome = "-PexpectedJavaHome=${javaHome}"
+
+        // Handle java on the system PATH, with no JAVA_HOME specified
+        String path = String.format('%s%s%s', Jvm.current().javaExecutable.parentFile, File.pathSeparator, System.getenv('PATH'))
+        executer.withEnvironmentVars('PATH': path, 'JAVA_HOME': '')
+                .withArguments(expectedJavaHome)
+                .withTasks('checkJavaHome')
+                .run()
+
+        // Handle JAVA_HOME specified
+        executer.withEnvironmentVars('JAVA_HOME': javaHome)
+                .withArguments(expectedJavaHome)
+                .withTasks('checkJavaHome')
+                .run()
+
+        // Handle JAVA_HOME with trailing separator
+        executer.withEnvironmentVars('JAVA_HOME': javaHome + File.separator)
+                .withArguments(expectedJavaHome)
+                .withTasks('checkJavaHome')
+                .run()
+
+        if (!OperatingSystem.current().isWindows()) {
+            return
+        }
+
+        // Handle JAVA_HOME wrapped in quotes
+        executer.withEnvironmentVars('JAVA_HOME': "\"$javaHome\"")
+                .withArguments(expectedJavaHome)
+                .withTasks('checkJavaHome')
+                .run()
+
+        // Handle JAVA_HOME with slash separators. This is allowed by the JVM
+        executer.withEnvironmentVars('JAVA_HOME': javaHome.replace(File.separator, '/'))
+                .withArguments(expectedJavaHome)
+                .withTasks('checkJavaHome')
+                .run()
+    }
+
+    @Test
+    public void failsWhenJavaHomeDoetNotPointToAJavaInstallation() {
+        def failure = executer.withEnvironmentVars('JAVA_HOME': dist.testDir)
+                .withTasks('checkJavaHome')
+                .runWithFailure()
+        assert failure.output.contains('ERROR: JAVA_HOME is set to an invalid directory')
+    }
+
+    @Test
+    public void canDefineGradleUserHomeViaEnvironmentVariable() {
+        // the actual testing is done in the build script.
+        File gradleUserHomeDir = dist.testDir.file('customUserHome')
+        executer.withUserHomeDir(null)
+                .withEnvironmentVars('GRADLE_USER_HOME': gradleUserHomeDir.absolutePath)
+                .withTasks("checkGradleUserHomeViaSystemEnv")
+                .run();
+    }
+
+    @Test
+    public void checkDefaultGradleUserHome() {
+        // the actual testing is done in the build script.
+        executer.withUserHomeDir(null).
+                withTasks("checkDefaultGradleUserHome")
+                .run();
+    }
+
+    @Test
+    public void canSpecifySystemPropertyFromCommandLine() {
+        // the actual testing is done in the build script.
+        executer.withTasks("checkSystemProperty").withArguments('-DcustomSystemProperty=custom-value').run();
+    }
+
+    @Test
+    public void canSpecifySystemPropertyUsingGradleOptsEnvironmentVariable() {
+        // the actual testing is done in the build script.
+        executer.withTasks("checkSystemProperty").withEnvironmentVars("GRADLE_OPTS": '-DcustomSystemProperty=custom-value').run();
+    }
+
+    @Test @Ignore
+    public void systemPropGradleUserHomeHasPrecedenceOverEnvVariable() {
+        // the actual testing is done in the build script.
+        File gradleUserHomeDir = dist.testFile("customUserHome")
+        File systemPropGradleUserHomeDir = dist.testFile("systemPropCustomUserHome")
+        executer.withUserHomeDir(null)
+                .withArguments("-Dgradle.user.home=" + systemPropGradleUserHomeDir.absolutePath)
+                .withEnvironmentVars('GRADLE_USER_HOME': gradleUserHomeDir.absolutePath)
+                .withTasks("checkSystemPropertyGradleUserHomeHasPrecedence")
+                .run()
+    }
+}
\ No newline at end of file
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CopyErrorIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CopyErrorIntegrationTest.groovy
new file mode 100644
index 0000000..28287fd
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CopyErrorIntegrationTest.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.integtests
+
+import org.gradle.integtests.fixtures.ExecutionFailure
+import org.gradle.util.OperatingSystem
+import org.gradle.util.TestFile
+import org.junit.Assert
+import org.junit.Test
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest
+
+class CopyErrorIntegrationTest extends AbstractIntegrationTest {
+    @Test
+    public void reportsSymLinkWhichPointsToNothing() {
+        if (OperatingSystem.current().isWindows()) {
+            return
+        }
+
+        TestFile link = testFile('src/file')
+        link.linkTo(testFile('missing'))
+
+        Assert.assertFalse(link.isDirectory())
+        Assert.assertFalse(link.isFile())
+        Assert.assertFalse(link.exists())
+
+        testFile('build.gradle') << '''
+            task copy(type: Copy) {
+                from 'src'
+                into 'dest'
+            }
+'''
+
+        ExecutionFailure failure = inTestDirectory().withTasks('copy').runWithFailure()
+        failure.assertHasDescription("Could not list contents of '${link}'.")
+    }
+
+    @Test
+    public void reportsUnreadableSourceDir() {
+        if (OperatingSystem.current().isWindows()) {
+            return
+        }
+
+        TestFile dir = testFile('src').createDir()
+        dir.permissions = '-w-r--r--'
+
+        Assert.assertTrue(dir.isDirectory())
+        Assert.assertTrue(dir.exists())
+        Assert.assertFalse(dir.canRead())
+        Assert.assertTrue(dir.canWrite())
+
+        testFile('build.gradle') << '''
+            task copy(type: Copy) {
+                from 'src'
+                into 'dest'
+            }
+'''
+
+        ExecutionFailure failure = inTestDirectory().withTasks('copy').runWithFailure()
+        failure.assertHasDescription("Could not list contents of directory '${dir}' as it is not readable.")
+
+        dir.permissions = 'rwxr--r--'
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CopyTaskIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CopyTaskIntegrationTest.groovy
new file mode 100644
index 0000000..7797fce
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CopyTaskIntegrationTest.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.integtests
+
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest
+import org.gradle.util.TestFile
+import org.junit.Rule
+import org.junit.Test
+import static org.hamcrest.Matchers.*
+import static org.junit.Assert.*
+
+public class CopyTaskIntegrationTest extends AbstractIntegrationTest {
+    @Rule
+    public final TestResources resources = new TestResources("copyTestResources")
+
+    @Test
+    public void testSingleSourceWithIncludeAndExclude() {
+        TestFile buildFile = testFile("build.gradle") << '''
+            task (copy, type:Copy) {
+               from 'src'
+               into 'dest'
+               include '**/sub/**'
+               exclude '**/ignore/**'
+            }
+'''
+        usingBuildFile(buildFile).withTasks("copy").run()
+        testFile('dest').assertHasDescendants(
+                'one/sub/onesub.a',
+                'one/sub/onesub.b'
+        )
+    }
+
+   @Test
+   public void testSingleSourceWithSpecClosures() {
+       TestFile buildFile = testFile("build.gradle").writelns(
+               "task (copy, type:Copy) {",
+               "   from 'src'",
+               "   into 'dest'",
+               "   include { fte -> !fte.file.name.endsWith('b') }",
+               "   exclude { fte -> fte.file.name == 'bad.file' }",
+               "}"
+       )
+       usingBuildFile(buildFile).withTasks("copy").run()
+       testFile('dest').assertHasDescendants(
+               'root.a',
+               'one/one.a',
+               'two/two.a',
+       )
+   }
+
+    @Test
+    public void testMultipleSourceWithInheritedPatterns() {
+        TestFile buildFile = testFile("build.gradle") << '''
+            task (copy, type:Copy) {
+               into 'dest'
+               from('src/one') {
+                  into '1'
+                  include '**/*.a'
+               }
+               from('src/two') {
+                  into '2'
+                  include '**/*.b'
+               }
+               exclude '**/ignore/**'
+            }
+'''
+        usingBuildFile(buildFile).withTasks("copy").run()
+        testFile('dest').assertHasDescendants(
+                '1/one.a',
+                '1/sub/onesub.a',
+                '2/two.b',
+        )
+    }
+
+    @Test
+    public void testMultipleSourcesWithInheritedDestination() {
+        TestFile buildFile = testFile("build.gradle") << '''
+            task (copy, type:Copy) {
+               into 'dest'
+               into('common') {
+                  from('src/one') {
+                     into 'a/one'
+                     include '*.a'
+                  }
+                  into('b') {
+                     from('src/two') {
+                        into 'two'
+                        include '**/*.b'
+                     }
+                  }
+               }
+            }
+'''
+        usingBuildFile(buildFile).withTasks("copy").run()
+        testFile('dest').assertHasDescendants(
+                'common/a/one/one.a',
+                'common/b/two/two.b',
+        )
+    }
+
+    @Test void testRename() {
+        TestFile buildFile = testFile("build.gradle") << '''
+            task (copy, type:Copy) {
+               from 'src'
+               into 'dest'
+               exclude '**/ignore/**'
+               rename '(.*).a', '\$1.renamed'
+               rename { it.startsWith('one.') ? "renamed_$it" : it }
+            }
+'''
+        usingBuildFile(buildFile).withTasks("copy").run()
+        testFile('dest').assertHasDescendants(
+                'root.renamed',
+                'root.b',
+                'one/renamed_one.renamed',
+                'one/renamed_one.b',
+                'one/sub/onesub.renamed',
+                'one/sub/onesub.b',
+                'two/two.renamed',
+                'two/two.b'
+        )
+    }
+
+    @Test
+    public void testCopyAction() {
+        TestFile buildFile = testFile("build.gradle").writelns(
+                "task copyIt << {",
+                "   copy {",
+                "      from 'src'",
+                "      into 'dest'",
+                "      exclude '**/ignore/**'",
+                "   }",
+                "}"
+        )
+        usingBuildFile(buildFile).withTasks("copyIt").run()
+        testFile('dest').assertHasDescendants(
+                'root.a',
+                'root.b',
+                'one/one.a',
+                'one/one.b',
+                'one/sub/onesub.a',
+                'one/sub/onesub.b',
+                'two/two.a',
+                'two/two.b',
+        )
+    }
+
+    @Test public void copySingleFiles() {
+        TestFile buildFile = testFile("build.gradle").writelns(
+                "task copyIt << {",
+                "   copy {",
+                "      from 'src/one/one.a', 'src/two/two.a'",
+                "      into 'dest/two'",
+                "   }",
+                "}"
+        )
+        usingBuildFile(buildFile).withTasks("copyIt").run()
+        testFile('dest').assertHasDescendants(
+                'two/one.a',
+                'two/two.a',
+        )
+    }
+
+    /*
+     * two.a starts off with "$one\n${one+1}\n${one+1+1}\n"
+     * If these filters are chained in the correct order, you should get 6, 11, and 16
+     */
+    @Test public void copyMultipleFilterTest() {
+        TestFile buildFile = testFile('build.gradle').writelns(
+                """task (copy, type:Copy) {
+                   into 'dest'
+                   expand(one: 1)
+                   filter { (Integer.parseInt(it) * 10) as String }
+                   filter { (Integer.parseInt(it) + 2) as String }
+                   from('src/two/two.a') {
+                     filter { (Integer.parseInt(it) / 2) as String }
+                   }
+                }
+                """
+        )
+        usingBuildFile(buildFile).withTasks("copy").run()
+        Iterator<String> it = testFile('dest/two.a').readLines().iterator()
+        assertThat(it.next(), startsWith('6'))
+        assertThat(it.next(), startsWith('11'))
+        assertThat(it.next(), startsWith('16'))
+    }
+
+    @Test public void chainedTransformations() {
+        def buildFile = testFile('build.gradle') << '''
+            task copy(type: Copy) {
+                into 'dest'
+                rename '(.*).a', '\$1.renamed'
+                eachFile { fcd -> if (fcd.path.contains('/ignore/')) { fcd.exclude() } }
+                eachFile { fcd -> if (fcd.relativePath.segments.length > 1) { fcd.relativePath = fcd.relativePath.prepend('prefix') }}
+                filter(org.apache.tools.ant.filters.PrefixLines, prefix: 'line: ')
+                eachFile { fcd -> fcd.filter { it.replaceAll('^line:', 'prefix:') } }
+                from ('src') {
+                    rename '(.*).renamed', '\$1.renamed_twice'
+                    eachFile { fcd -> fcd.path = fcd.path.replaceAll('/one/sub/', '/one_sub/') }
+                    eachFile { fcd -> if (fcd.path.contains('/two/')) { fcd.exclude() } }
+                    eachFile { fcd -> fcd.filter { "[$it]" } }
+                }
+            }
+'''
+        usingBuildFile(buildFile).withTasks('copy').run()
+        testFile('dest').assertHasDescendants(
+                'root.renamed_twice',
+                'root.b',
+                'prefix/one/one.renamed_twice',
+                'prefix/one/one.b',
+                'prefix/one_sub/onesub.renamed_twice',
+                'prefix/one_sub/onesub.b'
+        )
+
+        Iterator<String> it = testFile('dest/root.renamed_twice').readLines().iterator()
+        assertThat(it.next(), equalTo('[prefix: line 1]'))
+        assertThat(it.next(), equalTo('[prefix: line 2]'))
+    }
+
+    @Test public void testCopyFromFileTree() {
+        TestFile buildFile = testFile("build.gradle").writelns(
+                """task cpy << {
+                   copy {
+                        from fileTree(dir: 'src', excludes: ['**/ignore/**'], includes: ['*', '*/*'])
+                        into 'dest'
+                    }
+                }"""
+        )
+        usingBuildFile(buildFile).withTasks("cpy").run()
+        testFile('dest').assertHasDescendants(
+                'root.a',
+                'root.b',
+                'one/one.a',
+                'one/one.b',
+                'two/two.a',
+                'two/two.b',
+        )
+    }
+
+    @Test public void testCopyFromFileCollection() {
+        TestFile buildFile = testFile("build.gradle").writelns(
+                """task copy << {
+                   copy {
+                        from files('src')
+                        into 'dest'
+                        exclude '**/ignore/**'
+                        exclude '*/*/*/**'
+                    }
+                }"""
+        )
+        usingBuildFile(buildFile).withTasks("copy").run()
+        testFile('dest').assertHasDescendants(
+                'root.a',
+                'root.b',
+                'one/one.a',
+                'one/one.b',
+                'two/two.a',
+                'two/two.b',
+        )
+    }
+
+    @Test public void testCopyFromCompositeFileCollection() {
+        testFile('a.jar').touch()
+
+        TestFile buildFile = testFile("build.gradle").writelns(
+                """
+                configurations { compile }
+                dependencies { compile files('a.jar') }
+                task copy << {
+                   copy {
+                        from files('src2') + fileTree { from 'src'; exclude '**/ignore/**' } + configurations.compile
+                        into 'dest'
+                        include { fte -> fte.relativePath.segments.length < 3 && (fte.file.directory || fte.file.name.contains('a')) }
+                    }
+                }"""
+        )
+        usingBuildFile(buildFile).withTasks("copy").run()
+        testFile('dest').assertHasDescendants(
+                'root.a',
+                'one/one.a',
+                'two/two.a',
+                'three/three.a',
+                'a.jar'
+        )
+    }
+
+    @Test public void testCopyWithCopyspec() {
+        TestFile buildFile = testFile("build.gradle").writelns(
+                """
+                def spec = copySpec {
+                    from 'src'
+                    exclude '**/ignore/**'
+                    include '*/*.a'
+                    into 'subdir'
+                }
+                task copy(type: Copy) {
+                    into 'dest'
+                    with spec
+                }"""
+        )
+        usingBuildFile(buildFile).withTasks("copy").run()
+        testFile('dest').assertHasDescendants(
+                'subdir/one/one.a',
+                'subdir/two/two.a'
+        )
+    }
+
+    // can't use TestResources here because Git doesn't support committing empty directories
+    @Test
+    void emptyDirsAreCopiedByDefault() {
+        file("src999", "emptyDir").createDir()
+        file("src999", "yet", "another", "veryEmptyDir").createDir()
+
+        // need to include a file in the copy, otherwise copy task says "no source files"
+        file("src999", "dummy").createFile()
+
+        def buildFile = testFile("build.gradle") <<
+                """
+                task copy(type: Copy) {
+                    from 'src999'
+                    into 'dest'
+                }
+                """
+        usingBuildFile(buildFile).withTasks("copy").run()
+
+        assert file("dest", "emptyDir").isDirectory()
+        assert file("dest", "emptyDir").list().size() == 0
+        assert file("dest", "yet", "another", "veryEmptyDir").isDirectory()
+        assert file("dest", "yet", "another", "veryEmptyDir").list().size() == 0
+    }
+
+    @Test
+    void emptyDirsAreNotCopiedIfCorrespondingOptionIsSetToFalse() {
+        file("src999", "emptyDir").createDir()
+        file("src999", "yet", "another", "veryEmptyDir").createDir()
+
+        // need to include a file in the copy, otherwise copy task says "no source files"
+        file("src999", "dummy").createFile()
+
+        def buildFile = testFile("build.gradle") <<
+                """
+                task copy(type: Copy) {
+                    from 'src999'
+                    into 'dest'
+
+                    includeEmptyDirs = false
+                }
+                """
+        usingBuildFile(buildFile).withTasks("copy").run()
+
+        assert !file("dest", "emptyDir").exists()
+        assert !file("dest", "yet", "another", "veryEmptyDir").exists()
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CrossVersionCompatibilityIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CrossVersionCompatibilityIntegrationTest.groovy
new file mode 100644
index 0000000..a8cc2a9
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CrossVersionCompatibilityIntegrationTest.groovy
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.integtests.fixtures.BasicGradleDistribution
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.util.Jvm
+import org.junit.Rule
+import org.junit.Test
+
+class CrossVersionCompatibilityIntegrationTest {
+    @Rule public final GradleDistribution dist = new GradleDistribution()
+    @Rule public final TestResources resources = new TestResources()
+    private final BasicGradleDistribution gradle08 = dist.previousVersion('0.8')
+    private final BasicGradleDistribution gradle09rc3 = dist.previousVersion('0.9-rc-3')
+    private final BasicGradleDistribution gradle09 = dist.previousVersion('0.9')
+    private final BasicGradleDistribution gradle091 = dist.previousVersion('0.9.1')
+    private final BasicGradleDistribution gradle092 = dist.previousVersion('0.9.2')
+    private final BasicGradleDistribution gradle10Milestone1 = dist.previousVersion('1.0-milestone-1')
+    private final BasicGradleDistribution gradle10Milestone2 = dist.previousVersion('1.0-milestone-2')
+
+    @Test
+    public void canBuildJavaProject() {
+        dist.testFile('buildSrc/src/main/groovy').assertIsDir()
+
+        // Upgrade and downgrade from previous version to current version and back again
+        eachVersion([gradle08, gradle09rc3, gradle09, gradle091, gradle092, gradle10Milestone1, gradle10Milestone2]) { version ->
+            version.executer().inDirectory(dist.testDir).withTasks('build').run()
+            dist.executer().inDirectory(dist.testDir).withTasks('build').run()
+            version.executer().inDirectory(dist.testDir).withTasks('build').run()
+        }
+    }
+
+    @Test
+    public void canUseWrapperFromPreviousVersionToRunCurrentVersion() {
+        eachVersion([gradle09rc3, gradle09, gradle091, gradle092, gradle10Milestone1, gradle10Milestone2]) { version ->
+            checkWrapperWorksWith(version, dist)
+        }
+    }
+
+    @Test
+    public void canUseWrapperFromCurrentVersionToRunPreviousVersion() {
+        eachVersion([gradle09rc3, gradle09, gradle091, gradle092, gradle10Milestone1, gradle10Milestone2]) { version ->
+            checkWrapperWorksWith(dist, version)
+        }
+    }
+
+    def checkWrapperWorksWith(BasicGradleDistribution wrapperGenVersion, BasicGradleDistribution executionVersion) {
+        wrapperGenVersion.executer().withTasks('wrapper').withArguments("-PdistZip=$executionVersion.binDistribution.absolutePath", "-PdistVersion=$executionVersion.version").run()
+        def result = wrapperGenVersion.executer().usingExecutable('gradlew').withTasks('hello').run()
+        assert result.output.contains("hello from $executionVersion.version")
+    }
+
+    def eachVersion(Iterable<BasicGradleDistribution> versions, Closure cl) {
+        versions.each { version ->
+            if (!version.worksWith(Jvm.current())) {
+                System.out.println("skipping $version as it does not work with ${Jvm.current()}.")
+                return
+            }
+            try {
+                System.out.println("building using $version");
+                cl.call(version)
+            } catch (Throwable t) {
+                throw new RuntimeException("Could not build test project using $version.", t)
+            }
+        }
+    }
+}
+
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/DependenciesResolveIntegrationTest.java b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/DependenciesResolveIntegrationTest.java
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/DependenciesResolveIntegrationTest.java
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/DependenciesResolveIntegrationTest.java
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/DistributionIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/DistributionIntegrationTest.groovy
new file mode 100644
index 0000000..969f6f0
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/DistributionIntegrationTest.groovy
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.apache.tools.ant.taskdefs.Expand
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.gradle.util.AntUtil
+import org.gradle.util.GradleVersion
+import org.gradle.util.TestFile
+import org.junit.Rule
+import org.junit.Test
+import static org.hamcrest.Matchers.containsString
+import static org.hamcrest.Matchers.equalTo
+import static org.junit.Assert.assertThat
+
+class DistributionIntegrationTest {
+    @Rule public final GradleDistribution dist = new GradleDistribution()
+    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+    private String version = GradleVersion.current().version
+
+    @Test
+    public void binZipContents() {
+        TestFile binZip = dist.distributionsDir.file("gradle-$version-bin.zip")
+        binZip.usingNativeTools().unzipTo(dist.testDir)
+        TestFile contentsDir = dist.testDir.file("gradle-$version")
+
+        checkMinimalContents(contentsDir)
+
+        // Extra stuff
+        contentsDir.file('src').assertDoesNotExist()
+        contentsDir.file('samples').assertDoesNotExist()
+        contentsDir.file('docs').assertDoesNotExist()
+    }
+
+    @Test
+    public void allZipContents() {
+        TestFile binZip = dist.distributionsDir.file("gradle-$version-all.zip")
+        binZip.usingNativeTools().unzipTo(dist.testDir)
+        TestFile contentsDir = dist.testDir.file("gradle-$version")
+
+        checkMinimalContents(contentsDir)
+
+        // Source
+        contentsDir.file('src/org/gradle/api/Project.java').assertIsFile()
+        contentsDir.file('src/org/gradle/initialization/defaultBuildSourceScript.txt').assertIsFile()
+        contentsDir.file('src/org/gradle/gradleplugin/userinterface/swing/standalone/BlockingApplication.java').assertIsFile()
+        contentsDir.file('src/org/gradle/wrapper/Wrapper.java').assertIsFile()
+
+        // Samples
+        contentsDir.file('samples/java/quickstart/build.gradle').assertIsFile()
+
+        // Javadoc
+        contentsDir.file('docs/javadoc/index.html').assertIsFile()
+        contentsDir.file('docs/javadoc/index.html').assertContents(containsString("Gradle API ${version}"))
+        contentsDir.file('docs/javadoc/org/gradle/api/Project.html').assertIsFile()
+
+        // Groovydoc
+        contentsDir.file('docs/groovydoc/index.html').assertIsFile()
+        contentsDir.file('docs/groovydoc/org/gradle/api/Project.html').assertIsFile()
+        contentsDir.file('docs/groovydoc/org/gradle/api/tasks/bundling/Zip.html').assertIsFile()
+
+        // Userguide
+        contentsDir.file('docs/userguide/userguide.html').assertIsFile()
+        contentsDir.file('docs/userguide/userguide.html').assertContents(containsString("<h3 class=\"releaseinfo\">Version ${version}</h3>"))
+        contentsDir.file('docs/userguide/userguide_single.html').assertIsFile()
+        contentsDir.file('docs/userguide/userguide_single.html').assertContents(containsString("<h3 class=\"releaseinfo\">Version ${version}</h3>"))
+//        contentsDir.file('docs/userguide/userguide.pdf').assertIsFile()
+
+        // DSL reference
+        contentsDir.file('docs/dsl/index.html').assertIsFile()
+        contentsDir.file('docs/dsl/index.html').assertContents(containsString("<title>Gradle DSL Version ${version}</title>"))
+    }
+
+    private def checkMinimalContents(TestFile contentsDir) {
+        // Check it can be executed
+        executer.inDirectory(contentsDir).usingExecutable('bin/gradle').withTaskList().run()
+
+        // Scripts
+        contentsDir.file('bin/gradle').assertIsFile()
+        contentsDir.file('bin/gradle.bat').assertIsFile()
+
+        // Top level files
+        contentsDir.file('LICENSE').assertIsFile()
+
+        // Libs
+        assertIsGradleJar(contentsDir.file("lib/gradle-core-${version}.jar"))
+        assertIsGradleJar(contentsDir.file("lib/gradle-ui-${version}.jar"))
+        assertIsGradleJar(contentsDir.file("lib/gradle-launcher-${version}.jar"))
+        assertIsGradleJar(contentsDir.file("lib/gradle-tooling-api-${version}.jar"))
+        assertIsGradleJar(contentsDir.file("lib/gradle-wrapper-${version}.jar"))
+
+        assertIsGradleJar(contentsDir.file("lib/plugins/gradle-plugins-${version}.jar"))
+        assertIsGradleJar(contentsDir.file("lib/plugins/gradle-ide-${version}.jar"))
+        assertIsGradleJar(contentsDir.file("lib/plugins/gradle-scala-${version}.jar"))
+        assertIsGradleJar(contentsDir.file("lib/plugins/gradle-code-quality-${version}.jar"))
+        assertIsGradleJar(contentsDir.file("lib/plugins/gradle-antlr-${version}.jar"))
+        assertIsGradleJar(contentsDir.file("lib/plugins/gradle-announce-${version}.jar"))
+        assertIsGradleJar(contentsDir.file("lib/plugins/gradle-jetty-${version}.jar"))
+        assertIsGradleJar(contentsDir.file("lib/plugins/gradle-sonar-${version}.jar"))
+        assertIsGradleJar(contentsDir.file("lib/plugins/gradle-maven-${version}.jar"))
+        assertIsGradleJar(contentsDir.file("lib/plugins/gradle-osgi-${version}.jar"))
+
+        // Docs
+        contentsDir.file('getting-started.html').assertIsFile()
+    }
+
+    private def assertIsGradleJar(TestFile jar) {
+        jar.assertIsFile()
+        assertThat(jar.manifest.mainAttributes.getValue('Implementation-Version'), equalTo(version))
+        assertThat(jar.manifest.mainAttributes.getValue('Implementation-Title'), equalTo('Gradle'))
+    }
+
+    @Test
+    public void sourceZipContents() {
+        TestFile srcZip = dist.distributionsDir.file("gradle-$version-src.zip")
+        srcZip.usingNativeTools().unzipTo(dist.testDir)
+        TestFile contentsDir = dist.testDir.file("gradle-$version")
+
+        // Build self using wrapper in source distribution
+        executer.inDirectory(contentsDir).usingExecutable('gradlew').withTasks('binZip').run()
+
+        File binZip = contentsDir.file('build/distributions').listFiles()[0]
+        Expand unpack = new Expand()
+        unpack.src = binZip
+        unpack.dest = contentsDir.file('build/distributions/unzip')
+        AntUtil.execute(unpack)
+        TestFile unpackedRoot = new TestFile(contentsDir.file('build/distributions/unzip').listFiles()[0])
+
+        // Make sure the build distribution does something useful
+        unpackedRoot.file("bin/gradle").assertIsFile()
+        // todo run something with the gradle build by the source dist
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/DynamicObjectIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/DynamicObjectIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/DynamicObjectIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/DynamicObjectIntegrationTest.groovy
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ExecIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ExecIntegrationTest.groovy
new file mode 100644
index 0000000..2651e25
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ExecIntegrationTest.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.integtests
+
+import org.gradle.integtests.fixtures.TestResources
+import org.junit.Rule
+import org.junit.Test
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest
+
+class ExecIntegrationTest extends AbstractIntegrationTest {
+    @Rule
+    public final TestResources testResources = new TestResources()
+
+    @Test
+    public void canExecuteJava() {
+        File buildFile = testFile("canExecuteJava.gradle");
+        usingBuildFile(buildFile).run();
+    }
+
+    @Test
+    public void canExecuteCommands() {
+        File buildFile = testFile("canExecuteCommands.gradle");
+        usingBuildFile(buildFile).run();
+    }
+
+
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ExternalPluginIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ExternalPluginIntegrationTest.groovy
new file mode 100644
index 0000000..beabf67
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ExternalPluginIntegrationTest.groovy
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.integtests.fixtures.ArtifactBuilder
+import org.junit.Test
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest
+
+public class ExternalPluginIntegrationTest extends AbstractIntegrationTest {
+    @Test
+    public void canReferencePluginInBuildSrcProjectById() {
+        testFile('buildSrc/src/main/java/CustomPlugin.java') << '''
+import org.gradle.api.*;
+public class CustomPlugin implements Plugin<Project> {
+    public void apply(Project p) { p.setProperty("prop", "value"); }
+}
+'''
+        testFile('buildSrc/src/main/resources/META-INF/gradle-plugins/custom.properties') << '''
+implementation-class=CustomPlugin
+'''
+
+        testFile('build.gradle') << '''
+apply plugin: 'custom'
+assert 'value' == prop
+task test
+'''
+        inTestDirectory().withTasks('test').run()
+    }
+    
+    @Test
+    public void canReferencePluginInExternalJarById() {
+        ArtifactBuilder builder = artifactBuilder()
+        builder.sourceFile('CustomPlugin.java') << '''
+import org.gradle.api.*;
+public class CustomPlugin implements Plugin<Project> {
+    public void apply(Project p) { p.setProperty("prop", "value"); }
+}
+'''
+        builder.resourceFile('META-INF/gradle-plugins/custom.properties') << '''
+implementation-class=CustomPlugin
+'''
+        builder.buildJar(testFile('external.jar'))
+
+        testFile('build.gradle') << '''
+buildscript {
+    dependencies {
+        classpath files('external.jar')
+    }
+}
+apply plugin: 'custom'
+assert 'value' == prop
+task test
+'''
+        inTestDirectory().withTasks('test').run()
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ExternalScriptErrorIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ExternalScriptErrorIntegrationTest.groovy
new file mode 100644
index 0000000..4ed8fe5
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ExternalScriptErrorIntegrationTest.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.integtests
+
+import org.gradle.integtests.fixtures.ExecutionFailure
+import org.gradle.util.TestFile
+import org.junit.Test
+import static org.hamcrest.Matchers.*
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest
+
+class ExternalScriptErrorIntegrationTest extends AbstractIntegrationTest {
+    @Test
+    public void reportsScriptEvaluationFailsWithGroovyException() {
+        testFile('build.gradle') << '''
+apply { from 'other.gradle' }
+'''
+        TestFile script = testFile('other.gradle') << '''
+
+doStuff()
+'''
+
+        ExecutionFailure failure = inTestDirectory().runWithFailure()
+
+        failure.assertHasFileName("Script '${script}'");
+        failure.assertHasLineNumber(3);
+        failure.assertHasDescription('A problem occurred evaluating script.');
+        failure.assertHasCause('Could not find method doStuff() for arguments [] on root project');
+    }
+
+    @Test
+    public void reportsScriptCompilationException() {
+        testFile('build.gradle') << '''
+apply { from 'other.gradle' }
+'''
+        TestFile script = testFile('other.gradle')
+        script.text = 'import org.gradle()'
+
+        ExecutionFailure failure = inTestDirectory().runWithFailure()
+        failure.assertHasFileName("Script '${script}'");
+        failure.assertHasLineNumber(1);
+        failure.assertHasDescription("Could not compile script '${script}'");
+        failure.assertThatCause(containsString("script '${script}': 1: unexpected token: ("))
+    }
+
+    @Test
+    public void reportsMissingScript() {
+        TestFile buildScript = testFile('build.gradle') << '''
+apply { from 'unknown.gradle' }
+'''
+        TestFile script = testFile('unknown.gradle')
+
+        ExecutionFailure failure = inTestDirectory().runWithFailure()
+        failure.assertHasFileName("Build file '${buildScript}");
+        failure.assertHasLineNumber(2);
+        failure.assertHasDescription("A problem occurred evaluating root project");
+        failure.assertHasCause("Could not read script '${script}' as it does not exist.");
+    }
+
+    @Test
+    public void reportsTaskExecutionFailsWithRuntimeException() {
+        testFile('build.gradle') << '''
+apply { from 'other.gradle' }
+'''
+        TestFile script = testFile('other.gradle') << '''
+task doStuff << {
+    throw new RuntimeException('fail')
+}
+'''
+
+        ExecutionFailure failure = inTestDirectory().withTasks('doStuff').runWithFailure()
+
+        failure.assertHasFileName("Script '${script}'");
+        failure.assertHasLineNumber(3);
+        failure.assertHasDescription('Execution failed for task \':doStuff\'');
+        failure.assertHasCause('fail');
+    }
+
+}
+
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ExternalScriptExecutionIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ExternalScriptExecutionIntegrationTest.groovy
new file mode 100644
index 0000000..b5e1593
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ExternalScriptExecutionIntegrationTest.groovy
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.integtests.fixtures.ArtifactBuilder
+import org.gradle.integtests.fixtures.ExecutionResult
+import org.gradle.integtests.fixtures.HttpServer
+import org.gradle.util.TestFile
+import org.junit.Test
+import static org.hamcrest.Matchers.*
+import static org.junit.Assert.*
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest
+import org.junit.Rule
+
+public class ExternalScriptExecutionIntegrationTest extends AbstractIntegrationTest {
+    @Rule
+    public final HttpServer server = new HttpServer()
+
+    @Test
+    public void executesExternalScriptAgainstAProjectWithCorrectEnvironment() {
+        createExternalJar()
+        createBuildSrc()
+
+        TestFile externalScript = testFile('external.gradle')
+        externalScript << """
+            buildscript {
+                dependencies { classpath files('repo/test-1.3.jar') }
+            }
+            new org.gradle.test.BuildClass()
+            new BuildSrcClass()
+            println 'quiet message'
+            captureStandardOutput(LogLevel.ERROR)
+            println 'error message'
+            assert project != null
+            assert "${externalScript.absolutePath.replace("\\", "\\\\")}" == buildscript.sourceFile as String
+            assert "${externalScript.toURI()}" == buildscript.sourceURI as String
+            assert buildscript.classLoader == getClass().classLoader.parent
+            assert buildscript.classLoader == Thread.currentThread().contextClassLoader
+            assert gradle.scriptClassLoader == buildscript.classLoader.parent
+            assert project.buildscript.classLoader != buildscript.classLoader
+            task doStuff
+            someProp = 'value'
+"""
+        testFile('build.gradle') << '''
+apply { from 'external.gradle' }
+assert 'value' == someProp
+'''
+
+        ExecutionResult result = inTestDirectory().withTasks('doStuff').run()
+        assertThat(result.output, containsString('quiet message'))
+        assertThat(result.output, not(containsString('error message')))
+        assertThat(result.error, containsString('error message'))
+        assertThat(result.error, not(containsString('quiet message')))
+    }
+
+    @Test
+    public void canExecuteExternalScriptAgainstAnArbitraryObject() {
+        createBuildSrc()
+
+        testFile('external.gradle') << '''
+println 'quiet message'
+captureStandardOutput(LogLevel.ERROR)
+println 'error message'
+new BuildSrcClass()
+assert 'doStuff' == name
+assert buildscript.classLoader == getClass().classLoader.parent
+assert buildscript.classLoader == Thread.currentThread().contextClassLoader
+assert project.gradle.scriptClassLoader == buildscript.classLoader.parent
+assert project.buildscript.classLoader != buildscript.classLoader
+someProp = 'value'
+'''
+        testFile('build.gradle') << '''
+task doStuff
+apply {
+    to doStuff
+    from 'external.gradle'
+}
+assert 'value' == doStuff.someProp
+'''
+
+        ExecutionResult result = inTestDirectory().withTasks('doStuff').run()
+        assertThat(result.output, containsString('quiet message'))
+        assertThat(result.output, not(containsString('error message')))
+        assertThat(result.error, containsString('error message'))
+        assertThat(result.error, not(containsString('quiet message')))
+    }
+
+    @Test
+    public void canExecuteExternalScriptFromSettingsScript() {
+        testFile('settings.gradle') << ''' apply { from 'other.gradle' } '''
+        testFile('other.gradle') << ''' include 'child' '''
+        testFile('build.gradle') << ''' assert ['child'] == subprojects*.name '''
+
+        inTestDirectory().withTaskList().run()
+    }
+
+    @Test
+    public void canExecuteExternalScriptFromInitScript() {
+        TestFile initScript = testFile('init.gradle') << ''' apply { from 'other.gradle' } '''
+        testFile('other.gradle') << '''
+addListener(new ListenerImpl())
+class ListenerImpl extends BuildAdapter {
+    public void projectsEvaluated(Gradle gradle) {
+        gradle.rootProject.task('doStuff')
+    }
+}
+'''
+        inTestDirectory().usingInitScript(initScript).withTasks('doStuff').run()
+    }
+
+    @Test
+    public void canExecuteExternalScriptFromExternalScript() {
+        testFile('build.gradle') << ''' apply { from 'other1.gradle' } '''
+        testFile('other1.gradle') << ''' apply { from 'other2.gradle' } '''
+        testFile('other2.gradle') << ''' task doStuff '''
+
+        inTestDirectory().withTasks('doStuff').run()
+    }
+
+    @Test
+    public void canFetchScriptViaHttp() {
+        TestFile script = testFile('external.gradle')
+
+        server.expectGet('/external.gradle', script)
+        server.start()
+
+        script << """
+            task doStuff
+            assert buildscript.sourceFile == null
+            assert "http://localhost:$server.port/external.gradle" == buildscript.sourceURI as String
+"""
+
+        testFile('build.gradle') << """
+            apply from: 'http://localhost:$server.port/external.gradle'
+            defaultTasks 'doStuff'
+"""
+
+        inTestDirectory().run()
+    }
+
+    @Test
+    public void cachesScriptClassForAGivenScript() {
+        testFile('settings.gradle') << 'include \'a\', \'b\''
+        testFile('external.gradle') << 'appliedScript = this'
+        testFile('build.gradle') << '''
+allprojects {
+   apply from: "$rootDir/external.gradle"
+}
+subprojects {
+    assert appliedScript.class == rootProject.appliedScript.class
+}
+task doStuff
+'''
+        inTestDirectory().withTasks('doStuff').run()
+    }
+
+    private TestFile createBuildSrc() {
+        return testFile('buildSrc/src/main/java/BuildSrcClass.java') << '''
+            public class BuildSrcClass { }
+'''
+    }
+
+    private def createExternalJar() {
+        ArtifactBuilder builder = artifactBuilder();
+        builder.sourceFile('org/gradle/test/BuildClass.java') << '''
+            package org.gradle.test;
+            public class BuildClass { }
+'''
+        builder.buildJar(testFile("repo/test-1.3.jar"))
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/FileTreeCopyIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/FileTreeCopyIntegrationTest.groovy
new file mode 100644
index 0000000..ebd4301
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/FileTreeCopyIntegrationTest.groovy
@@ -0,0 +1,83 @@
+/*
+ * 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.integtests.fixtures.TestResources
+import org.gradle.util.TestFile
+import org.junit.Rule
+import org.junit.Test
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest
+
+public class FileTreeCopyIntegrationTest extends AbstractIntegrationTest {
+    @Rule
+    public final TestResources resources = new TestResources("copyTestResources")
+
+    @Test public void testCopyWithClosure() {
+        TestFile buildFile = testFile("build.gradle").writelns(
+                """task cpy << {
+                   fileTree {
+                      from 'src'
+                      exclude '**/ignore/**'
+                   }.copy { into 'dest'}
+                }"""
+        )
+        usingBuildFile(buildFile).withTasks("cpy").run()
+        testFile('dest').assertHasDescendants(
+                'root.a',
+                'root.b',
+                'one/one.a',
+                'one/one.b',
+                'one/sub/onesub.a',
+                'one/sub/onesub.b',
+                'two/two.a',
+                'two/two.b',
+        )
+    }
+
+    @Test public void testCopyWithMap() {
+        TestFile buildFile = testFile("build.gradle").writelns(
+                """task cpy << {
+                   fileTree(dir:'src', excludes:['**/ignore/**', '**/sub/**']).copy { into 'dest'}
+                }"""
+        )
+        usingBuildFile(buildFile).withTasks("cpy").run()
+        testFile('dest').assertHasDescendants(
+                'root.a',
+                'root.b',
+                'one/one.a',
+                'one/one.b',
+                'two/two.a',
+                'two/two.b',
+        )
+    }
+
+    @Test public void testCopyFluent() {
+        TestFile buildFile = testFile("build.gradle").writelns(
+                """task cpy << {
+                   fileTree(dir:'src').exclude('**/ignore/**', '**/sub/*.?').copy { into 'dest' }
+                }"""
+        )
+        usingBuildFile(buildFile).withTasks("cpy").run()
+        testFile('dest').assertHasDescendants(
+                'root.a',
+                'root.b',
+                'one/one.a',
+                'one/one.b',
+                'two/two.a',
+                'two/two.b',
+        )
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/GroovyProjectIntegrationTest.java b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/GroovyProjectIntegrationTest.java
new file mode 100644
index 0000000..6aca413
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/GroovyProjectIntegrationTest.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.integtests;
+
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest;
+import org.junit.Test;
+
+public class GroovyProjectIntegrationTest extends AbstractIntegrationTest {
+    @Test
+    public void handlesEmptyProject() {
+        testFile("build.gradle").writelns(
+                "apply plugin: 'groovy'"
+        );
+        inTestDirectory().withTasks("build").run();
+    }
+
+    @Test
+    public void handlesJavaSourceOnly() {
+        testFile("src/main/java/somepackage/SomeClass.java").writelns("public class SomeClass { }");
+        testFile("build.gradle").writelns("apply plugin: 'groovy'");
+        testFile("settings.gradle").write("rootProject.name='javaOnly'");
+        inTestDirectory().withTasks("build").run();
+        testFile("build/libs/javaOnly.jar").assertExists();
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IncrementalBuildIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IncrementalBuildIntegrationTest.groovy
new file mode 100644
index 0000000..5921f1a
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IncrementalBuildIntegrationTest.groovy
@@ -0,0 +1,379 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.util.TestFile
+import org.junit.Test
+import static org.hamcrest.Matchers.*
+import static org.junit.Assert.*
+import org.gradle.integtests.fixtures.TestResources
+import org.junit.Rule
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest
+
+class IncrementalBuildIntegrationTest extends AbstractIntegrationTest {
+    @Rule public final TestResources resource = new TestResources()
+
+    @Test
+    public void skipsTaskWhenOutputFileIsUpToDate() {
+        testFile('build.gradle') << '''
+task a(type: org.gradle.integtests.TransformerTask) {
+    inputFile = file('src.txt')
+    outputFile = file('src.a.txt')
+}
+task b(type: org.gradle.integtests.TransformerTask, dependsOn: a) {
+    inputFile = a.outputFile
+    outputFile = file('src.b.txt')
+}
+'''
+        TestFile inputFile = testFile('src.txt')
+        TestFile outputFileA = testFile('src.a.txt')
+        TestFile outputFileB = testFile('src.b.txt')
+
+        inputFile.text = 'content'
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
+
+        TestFile.Snapshot aSnapshot = outputFileA.snapshot()
+        TestFile.Snapshot bSnapshot = outputFileB.snapshot()
+        assertThat(outputFileA.text, equalTo('[content]'))
+        assertThat(outputFileB.text, equalTo('[[content]]'))
+
+        // No changes
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
+
+        outputFileA.assertHasNotChangedSince(aSnapshot)
+        outputFileB.assertHasNotChangedSince(bSnapshot)
+
+        // Update timestamp, no content changes
+
+        inputFile.setLastModified(inputFile.lastModified() - 10000);
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
+
+        outputFileA.assertHasNotChangedSince(aSnapshot)
+        outputFileB.assertHasNotChangedSince(bSnapshot)
+
+        // Change content
+
+        inputFile.text = 'new content'
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
+
+        outputFileA.assertHasChangedSince(aSnapshot)
+        outputFileB.assertHasChangedSince(bSnapshot)
+        assertThat(outputFileA.text, equalTo('[new content]'))
+        assertThat(outputFileB.text, equalTo('[[new content]]'))
+
+        // Delete intermediate output file
+
+        outputFileA.delete()
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':b')
+
+        assertThat(outputFileA.text, equalTo('[new content]'))
+        assertThat(outputFileB.text, equalTo('[[new content]]'))
+
+        // Delete final output file
+
+        outputFileB.delete()
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a')
+
+        assertThat(outputFileA.text, equalTo('[new content]'))
+        assertThat(outputFileB.text, equalTo('[[new content]]'))
+
+        // Change build file in a way which does not affect the task
+
+        testFile('build.gradle').text += '''
+task c
+'''
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
+
+        // Change an input property of the first task (the content format)
+
+        testFile('build.gradle').text += '''
+a.format = ' %s '
+'''
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
+
+        assertThat(outputFileA.text, equalTo(' new content '))
+        assertThat(outputFileB.text, equalTo('[ new content ]'))
+
+        // Change final output file destination
+
+        testFile('build.gradle').text += '''
+b.outputFile = file('new-output.txt')
+'''
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a')
+        outputFileB = testFile('new-output.txt')
+        outputFileB.assertIsFile()
+
+        // Run with --no-opt command-line options
+        inTestDirectory().withTasks('b').withArguments('--no-opt').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
+
+        // Output files already exist before using this version of Gradle
+        // delete .gradle dir to simulate this
+        testFile('.gradle').assertIsDir().deleteDir()
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
+
+        outputFileB.delete()
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a')
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
+    }
+
+    @Test
+    public void skipsTaskWhenOutputDirContentsAreUpToDate() {
+        testFile('build.gradle') << '''
+task a(type: org.gradle.integtests.DirTransformerTask) {
+    inputDir = file('src')
+    outputDir = file('build/a')
+}
+task b(type: org.gradle.integtests.DirTransformerTask, dependsOn: a) {
+    inputDir = a.outputDir
+    outputDir = file('build/b')
+}
+'''
+
+        testFile('src').createDir()
+        testFile('src/file1.txt').write('content')
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
+
+        TestFile outputAFile = testFile('build/a/file1.txt')
+        TestFile outputBFile = testFile('build/b/file1.txt')
+        TestFile.Snapshot aSnapshot = outputAFile.snapshot()
+        TestFile.Snapshot bSnapshot = outputBFile.snapshot()
+
+        outputAFile.assertContents(equalTo('[content]'))
+        outputBFile.assertContents(equalTo('[[content]]'))
+
+        // No changes
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
+
+        outputAFile.assertHasNotChangedSince(aSnapshot)
+        outputBFile.assertHasNotChangedSince(bSnapshot)
+
+        // Change content
+
+        testFile('src/file1.txt').write('new content')
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
+
+        outputAFile.assertHasChangedSince(aSnapshot)
+        outputBFile.assertHasChangedSince(bSnapshot)
+        outputAFile.assertContents(equalTo('[new content]'))
+        outputBFile.assertContents(equalTo('[[new content]]'))
+
+        // Add file
+
+        testFile('src/file2.txt').write('content2')
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
+
+        testFile('build/a/file2.txt').assertContents(equalTo('[content2]'))
+        testFile('build/b/file2.txt').assertContents(equalTo('[[content2]]'))
+
+        // Remove file
+
+        testFile('src/file1.txt').delete()
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':b')
+
+        // Output files already exist before using this version of Gradle
+        // delete .gradle dir to simulate this
+        testFile('.gradle').assertIsDir().deleteDir()
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
+
+        testFile('build/b').deleteDir()
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a')
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
+    }
+
+    @Test
+    public void skipsTaskWhenInputPropertiesHaveNotChanged() {
+        testFile('build.gradle') << '''
+task a(type: org.gradle.integtests.GeneratorTask) {
+    text = project.text
+    outputFile = file('dest.txt')
+}
+'''
+
+        inTestDirectory().withTasks('a').withArguments('-Ptext=text').run().assertTasksExecuted(':a').assertTasksSkipped()
+
+        inTestDirectory().withTasks('a').withArguments('-Ptext=text').run().assertTasksExecuted(':a').assertTasksSkipped(':a')
+
+        inTestDirectory().withTasks('a').withArguments('-Ptext=newtext').run().assertTasksExecuted(':a').assertTasksSkipped()
+    }
+
+    @Test
+    public void multipleTasksCanGenerateIntoOverlappingOutputDirectories() {
+        testFile('build.gradle') << '''
+task a(type: org.gradle.integtests.DirTransformerTask) {
+    inputDir = file('src/a')
+    outputDir = file('build')
+}
+task b(type: org.gradle.integtests.DirTransformerTask) {
+    inputDir = file('src/b')
+    outputDir = file('build')
+}
+'''
+
+        testFile('src/a/file1.txt') << 'content'
+        testFile('src/b/file2.txt') << 'content'
+
+        inTestDirectory().withTasks('a', 'b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
+
+        // No changes
+
+        inTestDirectory().withTasks('a', 'b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
+
+        // Delete an output file
+
+        testFile('build/file1.txt').delete()
+
+        inTestDirectory().withTasks('a', 'b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':b')
+
+        // Change an output file
+
+        testFile('build/file2.txt').write('something else')
+
+        inTestDirectory().withTasks('a', 'b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a')
+
+        // Change to new version of Gradle
+        // Simulate this by removing the .gradle dir
+        testFile('.gradle').assertIsDir().deleteDir()
+
+        inTestDirectory().withTasks('a', 'b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
+
+        testFile('build').deleteDir()
+
+        inTestDirectory().withTasks('a').run().assertTasksExecuted(':a').assertTasksSkipped()
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':b').assertTasksSkipped()
+    }
+
+    @Test
+    public void canUseUpToDatePredicateToForceTaskToExecute() {
+        testFile('build.gradle') << '''
+task inputsAndOutputs {
+    inputs.files 'src.txt'
+    outputs.files 'src.a.txt'
+    outputs.upToDateWhen { project.hasProperty('uptodate') }
+    doFirst {
+        outputs.files.singleFile.text = "[${inputs.files.singleFile.text}]"
+    }
+}
+task noOutputs {
+    inputs.files 'src.txt'
+    outputs.upToDateWhen { project.hasProperty('uptodate') }
+    doFirst { }
+}
+task nothing {
+    outputs.upToDateWhen { project.hasProperty('uptodate') }
+    doFirst { }
+}
+'''
+        TestFile srcFile = testFile('src.txt')
+        srcFile.text = 'content'
+
+        // Task with input files, output files and a predicate
+        inTestDirectory().withTasks('inputsAndOutputs').run().assertTasksExecuted(':inputsAndOutputs').assertTasksSkipped()
+
+        // Is up to date
+        inTestDirectory().withArguments('-Puptodate').withTasks('inputsAndOutputs').run().assertTasksExecuted(':inputsAndOutputs').assertTasksSkipped(':inputsAndOutputs')
+
+        // Changed input file
+        srcFile.text = 'different'
+        inTestDirectory().withArguments('-Puptodate').withTasks('inputsAndOutputs').run().assertTasksExecuted(':inputsAndOutputs').assertTasksSkipped()
+
+        // Predicate is false
+        inTestDirectory().withTasks('inputsAndOutputs').run().assertTasksExecuted(':inputsAndOutputs').assertTasksSkipped()
+
+        // Task with input files and a predicate
+        inTestDirectory().withTasks('noOutputs').run().assertTasksExecuted(':noOutputs').assertTasksSkipped()
+
+        // Is up to date
+        inTestDirectory().withArguments('-Puptodate').withTasks('noOutputs').run().assertTasksExecuted(':noOutputs').assertTasksSkipped(':noOutputs')
+
+        // Changed input file
+        srcFile.text = 'different again'
+        inTestDirectory().withArguments('-Puptodate').withTasks('noOutputs').run().assertTasksExecuted(':noOutputs').assertTasksSkipped()
+
+        // Predicate is false
+        inTestDirectory().withTasks('noOutputs').run().assertTasksExecuted(':noOutputs').assertTasksSkipped()
+
+        // Task a predicate only
+        inTestDirectory().withTasks('nothing').run().assertTasksExecuted(':nothing').assertTasksSkipped()
+
+        // Is up to date
+        inTestDirectory().withArguments('-Puptodate').withTasks('nothing').run().assertTasksExecuted(':nothing').assertTasksSkipped(':nothing')
+
+        // Predicate is false
+        inTestDirectory().withTasks('nothing').run().assertTasksExecuted(':nothing').assertTasksSkipped()
+    }
+
+    @Test
+    public void lifecycleTaskIsUpToDateWhenAllDependenciesAreSkipped() {
+        testFile('build.gradle') << '''
+task a(type: org.gradle.integtests.TransformerTask) {
+    inputFile = file('src.txt')
+    outputFile = file('out.txt')
+}
+task b(dependsOn: a)
+'''
+
+        testFile('src.txt').text = 'content'
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
+    }
+
+    @Test
+    public void canShareArtifactsBetweenBuilds() {
+        def buildFile = testFile('build.gradle') << '''
+task otherBuild(type: GradleBuild) {
+    buildFile = 'build.gradle'
+    tasks = ['generate']
+    startParameter.searchUpwards = false
+}
+task transform(type: org.gradle.integtests.TransformerTask) {
+    dependsOn otherBuild
+    inputFile = file('generated.txt')
+    outputFile = file('out.txt')
+}
+task generate(type: org.gradle.integtests.TransformerTask) {
+    inputFile = file('src.txt')
+    outputFile = file('generated.txt')
+}
+'''
+        testFile('settings.gradle') << 'rootProject.name = "build"'
+        testFile('src.txt').text = 'content'
+
+        usingBuildFile(buildFile).withTasks('transform').run().assertTasksExecuted(':otherBuild', ':build:generate', ':transform').assertTasksSkipped()
+        usingBuildFile(buildFile).withTasks('transform').run().assertTasksExecuted(':otherBuild', ':build:generate', ':transform').assertTasksSkipped(':transform', ':build:generate')
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest.groovy
new file mode 100644
index 0000000..9ba3fd6
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest.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.integtests
+
+import org.gradle.integtests.fixtures.TestResources
+import org.junit.Rule
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.junit.Test
+import org.gradle.integtests.fixtures.ExecutionFailure
+
+class IncrementalGroovyCompileIntegrationTest {
+    @Rule public final GradleDistribution distribution = new GradleDistribution()
+    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+    @Rule public final TestResources resources = new TestResources()
+
+    @Test
+    public void recompilesSourceWhenPropertiesChange() {
+        executer.withTasks('compileGroovy').run().assertTasksSkipped(':compileJava')
+
+        distribution.testFile('build.gradle').text += '''
+            compileGroovy.options.debug = false
+'''
+
+        executer.withTasks('compileGroovy').run().assertTasksSkipped(':compileJava')
+
+        executer.withTasks('compileGroovy').run().assertTasksSkipped(':compileJava', ':compileGroovy')
+    }
+
+    @Test
+    public void recompilesDependentClasses() {
+        executer.withTasks("classes").run();
+
+        // Update interface, compile should fail
+        distribution.testFile('src/main/groovy/IPerson.groovy').assertIsFile().copyFrom(distribution.testFile('NewIPerson.groovy'))
+
+        ExecutionFailure failure = executer.withTasks("classes").runWithFailure();
+        failure.assertHasDescription("Execution failed for task ':compileGroovy'.");
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IncrementalGroovyProjectBuildIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IncrementalGroovyProjectBuildIntegrationTest.groovy
new file mode 100644
index 0000000..b1adb7c
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IncrementalGroovyProjectBuildIntegrationTest.groovy
@@ -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.integtests
+
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.gradle.util.TestFile
+import org.junit.Rule
+import org.junit.Test
+
+class IncrementalGroovyProjectBuildIntegrationTest {
+    @Rule public final GradleDistribution distribution = new GradleDistribution()
+    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+
+    @Test
+    public void doesNotRebuildGroovydocIfSourceHasNotChanged() {
+        distribution.testFile("src/main/groovy/BuildClass.java") << 'public class BuildClass { }'
+        distribution.testFile("build.gradle") << '''
+            apply plugin: 'groovy'
+            dependencies { groovy localGroovy() }
+            groovydoc {
+                link('http://download.oracle.com/javase/1.5.0/docs/api', 'java.,org.xml.,javax.,org.xml.')
+            }
+'''
+
+        executer.withTasks("groovydoc").run();
+
+        TestFile indexFile = distribution.testFile("build/docs/groovydoc/index.html");
+        indexFile.assertIsFile();
+        TestFile.Snapshot snapshot = indexFile.snapshot();
+
+        executer.withTasks("groovydoc").run().assertTaskSkipped(':groovydoc');
+
+        indexFile.assertHasNotChangedSince(snapshot);
+
+        distribution.testFile("build.gradle").append("groovydoc.link('http://download.oracle.com/javase/1.5.0/docs/api', 'java.')")
+
+        executer.withTasks("groovydoc").run().assertTaskNotSkipped(':groovydoc');
+
+        executer.withTasks("groovydoc").run().assertTaskSkipped(':groovydoc');
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/IncrementalJavaCompileIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IncrementalJavaCompileIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/IncrementalJavaCompileIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IncrementalJavaCompileIntegrationTest.groovy
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/IncrementalJavaProjectBuildIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IncrementalJavaProjectBuildIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/IncrementalJavaProjectBuildIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IncrementalJavaProjectBuildIntegrationTest.groovy
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IncrementalScalaCompileIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IncrementalScalaCompileIntegrationTest.groovy
new file mode 100644
index 0000000..b31a2ff
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IncrementalScalaCompileIntegrationTest.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.integtests
+
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.gradle.integtests.fixtures.TestResources
+import org.junit.Rule
+import org.junit.Test
+import org.gradle.integtests.fixtures.ExecutionFailure
+
+class IncrementalScalaCompileIntegrationTest {
+    @Rule public final GradleDistribution distribution = new GradleDistribution()
+    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+    @Rule public final TestResources resources = new TestResources()
+
+    @Test
+    public void recompilesSourceWhenPropertiesChange() {
+        executer.withTasks('compileScala').run().assertTasksSkipped(':compileJava')
+
+        distribution.testFile('build.gradle').text += '''
+            compileScala.options.debug = false
+'''
+
+        executer.withTasks('compileScala').run().assertTasksSkipped(':compileJava')
+
+        executer.withTasks('compileScala').run().assertTasksSkipped(':compileJava', ':compileScala')
+    }
+
+    @Test
+    public void recompilesDependentClasses() {
+        executer.withTasks("classes").run();
+
+        // Update interface, compile should fail
+        distribution.testFile('src/main/scala/IPerson.scala').assertIsFile().copyFrom(distribution.testFile('NewIPerson.scala'))
+
+        ExecutionFailure failure = executer.withTasks("classes").runWithFailure();
+        failure.assertHasDescription("Execution failed for task ':compileScala'.");
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/IncrementalTestIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IncrementalTestIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/IncrementalTestIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IncrementalTestIntegrationTest.groovy
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/InitScriptErrorIntegrationTest.java b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/InitScriptErrorIntegrationTest.java
new file mode 100644
index 0000000..3204b77
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/InitScriptErrorIntegrationTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.integtests.fixtures.ExecutionFailure;
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest;
+import org.gradle.util.TestFile;
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.containsString;
+
+public class InitScriptErrorIntegrationTest extends AbstractIntegrationTest {
+    @Test
+    public void reportsInitScriptEvaluationFailsWithGroovyException() {
+        TestFile initScript = testFile("init.gradle");
+        initScript.write("\ncreateTakk('do-stuff')");
+        ExecutionFailure failure = inTestDirectory().usingInitScript(initScript).runWithFailure();
+
+        failure.assertHasFileName(String.format("Initialization script '%s'", initScript));
+        failure.assertHasLineNumber(2);
+        failure.assertHasDescription("A problem occurred evaluating initialization script.");
+        failure.assertHasCause("No signature of method: org.gradle.invocation.DefaultGradle.createTakk() is applicable for argument types: (java.lang.String) values: [do-stuff]");
+    }
+
+    @Test
+    public void reportsGroovyCompilationException() {
+        TestFile initScript = testFile("init.gradle");
+        initScript.writelns(
+            "// a comment",
+            "import org.gradle.unknown.Unknown",
+            "new Unknown()");
+        ExecutionFailure failure = inTestDirectory().usingInitScript(initScript).runWithFailure();
+        failure.assertHasFileName(String.format("Initialization script '%s'", initScript));
+        failure.assertHasLineNumber(2);
+        failure.assertHasDescription(String.format("Could not compile initialization script '%s'.", initScript));
+        failure.assertThatCause(containsString(String.format("initialization script '%s': 2: unable to resolve class org.gradle.unknown.Unknown", initScript)));
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/InitScriptExecutionIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/InitScriptExecutionIntegrationTest.groovy
new file mode 100644
index 0000000..594f7f5
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/InitScriptExecutionIntegrationTest.groovy
@@ -0,0 +1,88 @@
+/*
+ * 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.integtests.fixtures.ArtifactBuilder
+import org.gradle.integtests.fixtures.ExecutionResult
+import org.gradle.util.TestFile
+import org.junit.Test
+import static org.hamcrest.Matchers.*
+import static org.junit.Assert.*
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest
+
+class InitScriptExecutionIntegrationTest extends AbstractIntegrationTest {
+    @Test
+    public void executesInitScriptWithCorrectEnvironment() {
+        createExternalJar();
+
+        TestFile initScript = testFile('init.gradle')
+        initScript << '''
+initscript {
+    dependencies { classpath files('repo/test-1.3.jar') }
+}
+new org.gradle.test.BuildClass()
+println 'quiet message'
+captureStandardOutput(LogLevel.ERROR)
+println 'error message'
+assert gradle != null
+assert initscript.classLoader == getClass().classLoader.parent
+assert initscript.classLoader == Thread.currentThread().contextClassLoader
+assert scriptClassLoader == initscript.classLoader.parent
+assert Gradle.class.classLoader == scriptClassLoader.parent.parent
+'''
+        testFile('build.gradle') << 'task doStuff'
+
+        ExecutionResult result = inTestDirectory().usingInitScript(initScript).withTasks('doStuff').run()
+        assertThat(result.output, containsString('quiet message'))
+        assertThat(result.output, not(containsString('error message')))
+        assertThat(result.error, containsString('error message'))
+        assertThat(result.error, not(containsString('quiet message')))
+    }
+
+    @Test
+    public void eachScriptHasIndependentClassLoader() {
+        createExternalJar()
+
+        TestFile initScript1 = testFile('init1.gradle')
+        initScript1 << '''
+initscript {
+    dependencies { classpath files('repo/test-1.3.jar') }
+}
+new org.gradle.test.BuildClass()
+'''
+        TestFile initScript2 = testFile('init2.gradle')
+        initScript2 << '''
+try {
+    Class.forName('org.gradle.test.BuildClass')
+    fail()
+} catch (ClassNotFoundException e) {
+}
+'''
+
+        testFile('build.gradle') << 'task doStuff'
+
+       inTestDirectory().usingInitScript(initScript1).usingInitScript(initScript2)
+    }
+
+    private def createExternalJar() {
+        ArtifactBuilder builder = artifactBuilder();
+        builder.sourceFile('org/gradle/test/BuildClass.java') << '''
+            package org.gradle.test;
+            public class BuildClass { }
+'''
+        builder.buildJar(testFile("repo/test-1.3.jar"))
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IvyPublishIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IvyPublishIntegrationTest.groovy
new file mode 100644
index 0000000..da67faa
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IvyPublishIntegrationTest.groovy
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.gradle.integtests.fixtures.HttpServer
+import org.hamcrest.Matchers
+import org.junit.Rule
+import org.junit.Test
+
+public class IvyPublishIntegrationTest {
+    @Rule
+    public final GradleDistribution dist = new GradleDistribution()
+    @Rule
+    public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+    @Rule
+    public final HttpServer server = new HttpServer()
+
+    @Test
+    public void canPublishUsingAnonymousHttp() {
+        server.start()
+
+        dist.testFile("settings.gradle").text = 'rootProject.name = "publish"'
+        dist.testFile("build.gradle") << """
+apply plugin: 'java'
+version = '2'
+group = 'org.gradle'
+uploadArchives {
+    repositories {
+        ivy {
+            name = 'gradleReleases'
+            artifactPattern "http://localhost:${server.port}/[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]"
+        }
+    }
+}
+"""
+        def uploadedJar = dist.testFile('uploaded.jar')
+        def uploadedIvy = dist.testFile('uploaded.xml')
+        server.expectPut('/org.gradle/publish/2/publish-2.jar', uploadedJar)
+        server.expectPut('/org.gradle/publish/2/ivy-2.xml', uploadedIvy)
+
+        executer.withTasks("uploadArchives").run()
+
+        uploadedJar.assertIsCopyOf(dist.testFile('build/libs/publish-2.jar'))
+        uploadedIvy.assertIsFile()
+    }
+
+    @Test
+    public void canPublishUsingAuthenticatedHttp() {
+        server.start()
+
+        dist.testFile("settings.gradle").text = 'rootProject.name = "publish"'
+        dist.testFile("build.gradle") << """
+apply plugin: 'java'
+version = '2'
+group = 'org.gradle'
+uploadArchives {
+    repositories {
+        ivy {
+            name = 'gradleReleases'
+            userName = 'user'
+            password = 'password'
+            realm = 'test'
+            artifactPattern "http://localhost:${server.port}/[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]"
+        }
+    }
+}
+"""
+
+        def uploadedJar = dist.testFile('uploaded.jar')
+        def uploadedIvy = dist.testFile('uploaded.xml')
+        server.expectPut('/org.gradle/publish/2/publish-2.jar', 'user', 'password', uploadedJar)
+        server.expectPut('/org.gradle/publish/2/ivy-2.xml', 'user', 'password', uploadedIvy)
+
+        executer.withTasks("uploadArchives").run()
+
+        uploadedJar.assertIsCopyOf(dist.testFile('build/libs/publish-2.jar'))
+        uploadedIvy.assertIsFile()
+    }
+
+    @Test
+    public void reportsFailedHttpPublish() {
+        server.start()
+
+        dist.testFile("build.gradle") << """
+apply plugin: 'java'
+uploadArchives {
+    repositories {
+        ivy {
+            name = 'gradleReleases'
+            artifactPattern "http://localhost:${server.port}/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]"
+        }
+    }
+}
+"""
+
+        def result = executer.withTasks("uploadArchives").runWithFailure()
+        result.assertHasDescription('Execution failed for task \':uploadArchives\'.')
+        result.assertHasCause('Could not publish configurations [configuration \':archives\'].')
+        result.assertThatCause(Matchers.containsString('Received status code 404 from server: Not Found'))
+
+        server.stop()
+
+        result = executer.withTasks("uploadArchives").runWithFailure()
+        result.assertHasDescription('Execution failed for task \':uploadArchives\'.')
+        result.assertHasCause('Could not publish configurations [configuration \':archives\'].')
+        result.assertHasCause('java.net.ConnectException: Connection refused')
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/JUnitIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/JUnitIntegrationTest.groovy
new file mode 100644
index 0000000..656dc5a
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/JUnitIntegrationTest.groovy
@@ -0,0 +1,404 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.util.TestFile
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.gradle.integtests.fixtures.*
+import static org.gradle.util.Matchers.*
+import static org.hamcrest.Matchers.*
+import static org.junit.Assert.*
+
+public class JUnitIntegrationTest {
+    @Rule public final GradleDistribution dist = new GradleDistribution()
+    @Rule public final TestResources resources = new TestResources()
+    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+
+    @Test
+    public void executesTestsInCorrectEnvironment() {
+        TestFile testDir = dist.testDir;
+        executer.withTasks('build').run();
+
+        JUnitTestExecutionResult result = new JUnitTestExecutionResult(testDir)
+        result.assertTestClassesExecuted('org.gradle.OkTest', 'org.gradle.OtherTest')
+        result.testClass('org.gradle.OkTest').assertTestPassed('ok')
+        result.testClass('org.gradle.OkTest').assertStdout(containsString('This is test stdout'))
+        result.testClass('org.gradle.OkTest').assertStdout(containsString('no EOL'))
+        result.testClass('org.gradle.OkTest').assertStdout(containsString('class loaded'))
+        result.testClass('org.gradle.OkTest').assertStdout(containsString('test constructed'))
+        result.testClass('org.gradle.OkTest').assertStdout(containsString('stdout from another thread'))
+        result.testClass('org.gradle.OkTest').assertStderr(containsString('This is test stderr'))
+        result.testClass('org.gradle.OkTest').assertStderr(containsString('this is a warning'))
+        result.testClass('org.gradle.OtherTest').assertTestPassed('ok')
+        result.testClass('org.gradle.OtherTest').assertStdout(containsString('This is other stdout'))
+        result.testClass('org.gradle.OtherTest').assertStdout(containsString('other class loaded'))
+        result.testClass('org.gradle.OtherTest').assertStdout(containsString('other test constructed'))
+        result.testClass('org.gradle.OtherTest').assertStderr(containsString('This is other stderr'))
+        result.testClass('org.gradle.OtherTest').assertStderr(containsString('this is another warning'))
+    }
+
+    @Test
+    public void canRunMixOfJunit3And4Tests() {
+        resources.maybeCopy('JUnitIntegrationTest/junit3Tests')
+        resources.maybeCopy('JUnitIntegrationTest/junit4Tests')
+        executer.withTasks('check').run()
+
+        def result = new JUnitTestExecutionResult(dist.testDir)
+        result.assertTestClassesExecuted('org.gradle.Junit3Test', 'org.gradle.Junit4Test', 'org.gradle.IgnoredTest')
+        result.testClass('org.gradle.Junit3Test').assertTestsExecuted('testRenamesItself')
+        result.testClass('org.gradle.Junit3Test').assertTestPassed('testRenamesItself')
+        result.testClass('org.gradle.Junit4Test').assertTestsExecuted('ok')
+        result.testClass('org.gradle.Junit4Test').assertTestPassed('ok')
+        result.testClass('org.gradle.Junit4Test').assertTestsSkipped('broken')
+        result.testClass('org.gradle.IgnoredTest').assertTestsExecuted()
+    }
+
+    @Test
+    public void canRunTestsUsingJUnit3() {
+        resources.maybeCopy('JUnitIntegrationTest/junit3Tests')
+        executer.withTasks('check').run()
+
+        def result = new JUnitTestExecutionResult(dist.testDir)
+        result.assertTestClassesExecuted('org.gradle.Junit3Test')
+        result.testClass('org.gradle.Junit3Test').assertTestsExecuted('testRenamesItself')
+        result.testClass('org.gradle.Junit3Test').assertTestPassed('testRenamesItself')
+    }
+
+    @Test
+    public void canRunTestsUsingJUnit4_4() {
+        resources.maybeCopy('JUnitIntegrationTest/junit3Tests')
+        resources.maybeCopy('JUnitIntegrationTest/junit4Tests')
+        resources.maybeCopy('JUnitIntegrationTest/junit4_4Tests')
+        executer.withTasks('check').run()
+
+        def result = new JUnitTestExecutionResult(dist.testDir)
+        result.assertTestClassesExecuted('org.gradle.Junit3Test', 'org.gradle.Junit4Test', 'org.gradle.IgnoredTest')
+        result.testClass('org.gradle.Junit3Test').assertTestsExecuted('testRenamesItself')
+        result.testClass('org.gradle.Junit3Test').assertTestPassed('testRenamesItself')
+        result.testClass('org.gradle.Junit4Test').assertTestsExecuted('ok')
+        result.testClass('org.gradle.Junit4Test').assertTestPassed('ok')
+        result.testClass('org.gradle.Junit4Test').assertTestsSkipped('broken')
+        result.testClass('org.gradle.IgnoredTest').assertTestsExecuted()
+    }
+
+    @Test
+    public void reportsAndBreaksBuildWhenTestFails() {
+        TestFile testDir = dist.getTestDir();
+        ExecutionFailure failure = executer.withTasks('build').runWithFailure();
+
+        failure.assertHasDescription("Execution failed for task ':test'.");
+        failure.assertThatCause(startsWith('There were failing tests.'));
+
+        assertThat(failure.getError(), containsLine('Test org.gradle.BrokenTest FAILED'));
+        assertThat(failure.getError(), containsLine('Test org.gradle.BrokenBefore FAILED'));
+        assertThat(failure.getError(), containsLine('Test org.gradle.BrokenAfter FAILED'));
+        assertThat(failure.getError(), containsLine('Test org.gradle.BrokenBeforeAndAfter FAILED'));
+        assertThat(failure.getError(), containsLine('Test org.gradle.BrokenBeforeClass FAILED'));
+        assertThat(failure.getError(), containsLine('Test org.gradle.BrokenAfterClass FAILED'));
+        assertThat(failure.getError(), containsLine('Test org.gradle.BrokenConstructor FAILED'));
+        assertThat(failure.getError(), containsLine('Test org.gradle.BrokenException FAILED'));
+        assertThat(failure.getError(), containsLine('Test org.gradle.Unloadable FAILED'));
+
+        JUnitTestExecutionResult result = new JUnitTestExecutionResult(testDir)
+        result.assertTestClassesExecuted(
+                'org.gradle.BrokenTest',
+                'org.gradle.BrokenBefore',
+                'org.gradle.BrokenAfter',
+                'org.gradle.BrokenBeforeClass',
+                'org.gradle.BrokenAfterClass',
+                'org.gradle.BrokenBeforeAndAfter',
+                'org.gradle.BrokenConstructor',
+                'org.gradle.BrokenException',
+                'org.gradle.Unloadable')
+        result.testClass('org.gradle.BrokenTest').assertTestFailed('failure', equalTo('java.lang.AssertionError: failed'))
+        result.testClass('org.gradle.BrokenTest').assertTestFailed('broken', equalTo('java.lang.IllegalStateException'))
+        result.testClass('org.gradle.BrokenBeforeClass').assertTestFailed('classMethod', equalTo('java.lang.AssertionError: failed'))
+        result.testClass('org.gradle.BrokenAfterClass').assertTestFailed('classMethod', equalTo('java.lang.AssertionError: failed'))
+        result.testClass('org.gradle.BrokenBefore').assertTestFailed('ok', equalTo('java.lang.AssertionError: failed'))
+        result.testClass('org.gradle.BrokenAfter').assertTestFailed('ok', equalTo('java.lang.AssertionError: failed'))
+        result.testClass('org.gradle.BrokenBeforeAndAfter').assertTestFailed('ok', equalTo('java.lang.AssertionError: before failed'), equalTo('java.lang.AssertionError: after failed'))
+        result.testClass('org.gradle.BrokenConstructor').assertTestFailed('ok', equalTo('java.lang.AssertionError: failed'))
+        result.testClass('org.gradle.BrokenException').assertTestFailed('broken', startsWith('Could not determine failure message for exception of type org.gradle.BrokenException$BrokenRuntimeException: '))
+        result.testClass('org.gradle.Unloadable').assertTestFailed('initializationError', equalTo('java.lang.AssertionError: failed'))
+    }
+
+    @Test
+    public void canRunSingleTests() {
+        executer.withTasks('test').withArguments('-Dtest.single=Ok2').run()
+        def result = new JUnitTestExecutionResult(dist.testDir)
+        result.assertTestClassesExecuted('Ok2')
+
+        executer.withTasks('cleanTest', 'test').withArguments('-Dtest.single=Ok').run()
+        result.assertTestClassesExecuted('Ok', 'Ok2')
+
+        def failure = executer.withTasks('test').withArguments('-Dtest.single=DoesNotMatchAClass').runWithFailure()
+        failure.assertHasCause('Could not find matching test for pattern: DoesNotMatchAClass')
+
+        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();
+        testDir.file('settings.gradle').write("include 'a', 'b'");
+        testDir.file('b/build.gradle') << '''
+            apply plugin: 'java'
+            repositories { mavenCentral() }
+            dependencies { compile 'junit:junit:4.7' }
+        '''
+        testDir.file('b/src/main/java/org/gradle/AbstractTest.java') << '''
+            package org.gradle;
+            public abstract class AbstractTest {
+                @org.junit.Test public void ok() { }
+            }
+        '''
+        TestFile buildFile = testDir.file('a/build.gradle');
+        buildFile << '''
+            apply plugin: 'java'
+            repositories { mavenCentral() }
+            dependencies { testCompile project(':b') }
+        '''
+        testDir.file('a/src/test/java/org/gradle/SomeTest.java') << '''
+            package org.gradle;
+            public class SomeTest extends AbstractTest {
+            }
+        '''
+
+        executer.withTasks('a:test').run();
+
+        JUnitTestExecutionResult result = new JUnitTestExecutionResult(testDir.file('a'))
+        result.assertTestClassesExecuted('org.gradle.SomeTest')
+        result.testClass('org.gradle.SomeTest').assertTestPassed('ok')
+    }
+
+    @Test
+    public void canExcludeSuperClassesFromExecution() {
+        TestFile testDir = dist.getTestDir();
+        TestFile buildFile = testDir.file('build.gradle');
+        buildFile << '''
+            apply plugin: 'java'
+            repositories { mavenCentral() }
+            dependencies { testCompile 'junit:junit:4.7' }
+            test { exclude '**/BaseTest.*' }
+        '''
+        testDir.file('src/test/java/org/gradle/BaseTest.java') << '''
+            package org.gradle;
+            public class BaseTest {
+                @org.junit.Test public void ok() { }
+            }
+        '''
+        testDir.file('src/test/java/org/gradle/SomeTest.java') << '''
+            package org.gradle;
+            public class SomeTest extends BaseTest {
+            }
+        '''
+
+        executer.withTasks('test').run();
+
+        JUnitTestExecutionResult result = new JUnitTestExecutionResult(testDir)
+        result.assertTestClassesExecuted('org.gradle.SomeTest')
+        result.testClass('org.gradle.SomeTest').assertTestPassed('ok')
+    }
+
+    @Test
+    public void detectsTestClasses() {
+        executer.withTasks('test').run()
+
+        JUnitTestExecutionResult result = new JUnitTestExecutionResult(dist.testDir)
+        result.assertTestClassesExecuted('org.gradle.EmptyRunWithSubclass', 'org.gradle.TestsOnInner', 'org.gradle.TestsOnInner$SomeInner')
+        result.testClass('org.gradle.EmptyRunWithSubclass').assertTestsExecuted('ok')
+        result.testClass('org.gradle.EmptyRunWithSubclass').assertTestPassed('ok')
+        result.testClass('org.gradle.TestsOnInner').assertTestPassed('ok')
+        result.testClass('org.gradle.TestsOnInner$SomeInner').assertTestPassed('ok')
+    }
+
+    @Test
+    public void runsAllTestsInTheSameForkedJvm() {
+        TestFile testDir = dist.getTestDir();
+        testDir.file('build.gradle').writelns(
+                "apply plugin: 'java'",
+                "repositories { mavenCentral() }",
+                "dependencies { compile 'junit:junit:4.7' }"
+        );
+        testDir.file('src/test/java/org/gradle/AbstractTest.java').writelns(
+                "package org.gradle;",
+                "public abstract class AbstractTest {",
+                "    @org.junit.Test public void ok() {",
+                "        long time = java.lang.management.ManagementFactory.getRuntimeMXBean().getStartTime();",
+                "        System.out.println(String.format(\"VM START TIME = %s\", time));",
+                "    }",
+                "}");
+        testDir.file('src/test/java/org/gradle/SomeTest.java').writelns(
+                "package org.gradle;",
+                "public class SomeTest extends AbstractTest {",
+                "}");
+        testDir.file('src/test/java/org/gradle/SomeTest2.java').writelns(
+                "package org.gradle;",
+                "public class SomeTest2 extends AbstractTest {",
+                "}");
+
+        executer.withTasks('test').run();
+
+        TestFile results1 = testDir.file('build/test-results/TEST-org.gradle.SomeTest.xml');
+        TestFile results2 = testDir.file('build/test-results/TEST-org.gradle.SomeTest2.xml');
+        results1.assertIsFile();
+        results2.assertIsFile();
+        assertThat(results1.linesThat(containsString('VM START TIME =')).get(0), equalTo(results2.linesThat(containsString('VM START TIME =')).get(0)));
+    }
+
+    @Test
+    public void canSpecifyMaximumNumberOfTestClassesToExecuteInAForkedJvm() {
+        TestFile testDir = dist.getTestDir();
+        testDir.file('build.gradle').writelns(
+                "apply plugin: 'java'",
+                "repositories { mavenCentral() }",
+                "dependencies { compile 'junit:junit:4.7' }",
+                "test.forkEvery = 1"
+        );
+        testDir.file('src/test/java/org/gradle/AbstractTest.java').writelns(
+                "package org.gradle;",
+                "public abstract class AbstractTest {",
+                "    @org.junit.Test public void ok() {",
+                "        long time = java.lang.management.ManagementFactory.getRuntimeMXBean().getStartTime();",
+                "        System.out.println(String.format(\"VM START TIME = %s\", time));",
+                "    }",
+                "}");
+        testDir.file('src/test/java/org/gradle/SomeTest.java').writelns(
+                "package org.gradle;",
+                "public class SomeTest extends AbstractTest {",
+                "}");
+        testDir.file('src/test/java/org/gradle/SomeTest2.java').writelns(
+                "package org.gradle;",
+                "public class SomeTest2 extends AbstractTest {",
+                "}");
+
+        executer.withTasks('test').run();
+
+        TestFile results1 = testDir.file('build/test-results/TEST-org.gradle.SomeTest.xml');
+        TestFile results2 = testDir.file('build/test-results/TEST-org.gradle.SomeTest2.xml');
+        results1.assertIsFile();
+        results2.assertIsFile();
+        assertThat(results1.linesThat(containsString('VM START TIME =')).get(0), not(equalTo(results2.linesThat(
+                containsString('VM START TIME =')).get(0))));
+    }
+
+    @Test
+    public void canListenForTestResults() {
+        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.junit.Test public void fail() { org.junit.Assert.fail(\"message\"); }",
+                "@org.junit.Test public void knownError() { throw new RuntimeException(\"message\"); }",
+                "@org.junit.Test public void unknownError() throws AppException { throw new AppException(); }",
+                "}"
+        );
+        testDir.file('src/test/java/SomeOtherTest.java').writelns(
+                "public class SomeOtherTest {",
+                "@org.junit.Test public void pass() { }",
+                "}"
+        );
+
+        testDir.file('build.gradle') << '''
+            apply plugin: 'java'
+            repositories { mavenCentral() }
+            dependencies { testCompile 'junit:junit:4.7' }
+            def listener = new TestListenerImpl()
+            test.addTestListener(listener)
+            test.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] [$result.resultType] [$result.testCount]" }
+                void beforeTest(TestDescriptor test) { println "START [$test] [$test.name]" }
+                void afterTest(TestDescriptor test, TestResult result) { println "FINISH [$test] [$test.name] [$result.resultType] [$result.testCount] [$result.exception]" }
+            }
+        '''
+
+        ExecutionResult result = executer.withTasks("test").run();
+        assertThat(result.getOutput(), containsLine("START [tests] []"));
+        assertThat(result.getOutput(), containsLine("FINISH [tests] [] [FAILURE] [4]"));
+
+        assertThat(result.getOutput(), containsLine("START [test process 'Gradle Worker 1'] [Gradle Worker 1]"));
+        assertThat(result.getOutput(), containsLine("FINISH [test process 'Gradle Worker 1'] [Gradle Worker 1] [FAILURE] [4]"));
+
+        assertThat(result.getOutput(), containsLine("START [test class SomeOtherTest] [SomeOtherTest]"));
+        assertThat(result.getOutput(), containsLine("FINISH [test class SomeOtherTest] [SomeOtherTest] [SUCCESS] [1]"));
+        assertThat(result.getOutput(), containsLine("START [test pass(SomeOtherTest)] [pass]"));
+        assertThat(result.getOutput(), containsLine("FINISH [test pass(SomeOtherTest)] [pass] [SUCCESS] [1] [null]"));
+
+        assertThat(result.getOutput(), containsLine("START [test class SomeTest] [SomeTest]"));
+        assertThat(result.getOutput(), containsLine("FINISH [test class SomeTest] [SomeTest] [FAILURE] [3]"));
+        assertThat(result.getOutput(), containsLine("START [test fail(SomeTest)] [fail]"));
+        assertThat(result.getOutput(), containsLine("FINISH [test fail(SomeTest)] [fail] [FAILURE] [1] [java.lang.AssertionError: message]"));
+        assertThat(result.getOutput(), containsLine("START [test knownError(SomeTest)] [knownError]"));
+        assertThat(result.getOutput(), containsLine("FINISH [test knownError(SomeTest)] [knownError] [FAILURE] [1] [java.lang.RuntimeException: message]"));
+        assertThat(result.getOutput(), containsLine("START [test unknownError(SomeTest)] [unknownError]"));
+        assertThat(result.getOutput(), containsLine("FINISH [test unknownError(SomeTest)] [unknownError] [FAILURE] [1] [org.gradle.messaging.remote.internal.PlaceholderException: AppException: null]"));
+    }
+
+    @Test
+    public void canListenForTestResultsWhenJUnit3IsUsed() {
+        TestFile testDir = dist.getTestDir();
+        testDir.file('src/test/java/SomeTest.java').writelns(
+                "public class SomeTest extends junit.framework.TestCase {",
+                "public void testPass() { }",
+                "public void testFail() { junit.framework.Assert.fail(\"message\"); }",
+                "public void testError() { throw new RuntimeException(\"message\"); }",
+                "}"
+        );
+
+        testDir.file('build.gradle') << '''
+            apply plugin: 'java'
+            repositories { mavenCentral() }
+            dependencies { testCompile 'junit:junit:3.8' }
+            def listener = new TestListenerImpl()
+            test.addTestListener(listener)
+            test.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.exception]" }
+            }
+        '''
+
+        ExecutionResult result = executer.withTasks("test").run();
+        assertThat(result.getOutput(), containsLine("START [test class SomeTest] [SomeTest]"));
+        assertThat(result.getOutput(), containsLine("FINISH [test class SomeTest] [SomeTest]"));
+        assertThat(result.getOutput(), containsLine("START [test testPass(SomeTest)] [testPass]"));
+        assertThat(result.getOutput(), containsLine("FINISH [test testPass(SomeTest)] [testPass] [null]"));
+        assertThat(result.getOutput(), containsLine("START [test testFail(SomeTest)] [testFail]"));
+        assertThat(result.getOutput(), containsLine("FINISH [test testFail(SomeTest)] [testFail] [junit.framework.AssertionFailedError: message]"));
+        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/integ-test/src/integTest/groovy/org/gradle/integtests/JUnitTestExecutionResult.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/JUnitTestExecutionResult.groovy
new file mode 100644
index 0000000..b97d968
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/JUnitTestExecutionResult.groovy
@@ -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.integtests
+
+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 JUnitTestExecutionResult implements TestExecutionResult {
+    private final TestFile buildDir
+
+    def JUnitTestExecutionResult(TestFile projectDir, String buildDirName = 'build') {
+        this.buildDir = projectDir.file(buildDirName)
+    }
+
+    TestExecutionResult assertTestClassesExecuted(String... testClasses) {
+        Map<String, File> classes = findClasses()
+        assertThat(classes.keySet(), equalTo(testClasses as Set));
+        this
+    }
+
+    TestClassExecutionResult testClass(String testClass) {
+        return new JUnitTestClassExecutionResult(findTestClass(testClass), testClass)
+    }
+
+    private def findTestClass(String testClass) {
+        def classes = findClasses()
+        assertThat(classes.keySet(), hasItem(testClass))
+        def classFile = classes.get(testClass)
+        assertThat(classFile, notNullValue())
+        return new XmlSlurper().parse(classFile)
+    }
+
+    private def findClasses() {
+        buildDir.file('test-results').assertIsDir()
+        buildDir.file('reports/tests/index.html').assertIsFile()
+
+        Map<String, File> classes = [:]
+        buildDir.file('test-results').eachFile { File file ->
+            def matcher = (file.name =~ /TEST-(.+)\.xml/)
+            if (matcher.matches()) {
+                classes[matcher.group(1)] = file
+            }
+        }
+        return classes
+    }
+}
+
+class JUnitTestClassExecutionResult implements TestClassExecutionResult {
+    GPathResult testClassNode
+    String testClassName
+    boolean checked
+
+    def JUnitTestClassExecutionResult(GPathResult testClassNode, String testClassName) {
+        this.testClassNode = testClassNode
+        this.testClassName = testClassName
+    }
+
+    TestClassExecutionResult assertTestsExecuted(String... testNames) {
+        Map<String, Node> testMethods = findTests()
+        assertThat(testMethods.keySet(), equalTo(testNames as Set))
+        this
+    }
+
+    TestClassExecutionResult assertTestPassed(String name) {
+        Map<String, Node> testMethods = findTests()
+        assertThat(testMethods.keySet(), hasItem(name))
+        assertThat(testMethods[name].failure.size(), equalTo(0))
+        this
+    }
+
+    TestClassExecutionResult assertTestFailed(String name, Matcher<? super String>... messageMatchers) {
+        Map<String, Node> testMethods = findTests()
+        assertThat(testMethods.keySet(), hasItem(name))
+
+        def failures = testMethods[name].failure
+        assertThat("Expected ${messageMatchers.length} failures. Found: $failures", failures.size(), equalTo(messageMatchers.length))
+
+        for (int i = 0; i < messageMatchers.length; i++) {
+            assertThat(failures[i]. at message.text(), messageMatchers[i])
+        }
+        this
+    }
+
+    TestClassExecutionResult assertTestSkipped(String name) {
+        throw new UnsupportedOperationException()
+    }
+
+    TestClassExecutionResult assertTestsSkipped(String... testNames) {
+        Map<String, Node> testMethods = findIgnoredTests()
+        assertThat(testMethods.keySet(), equalTo(testNames as Set))
+        this
+    }
+
+    TestClassExecutionResult assertConfigMethodPassed(String name) {
+        throw new UnsupportedOperationException();
+    }
+
+    TestClassExecutionResult assertConfigMethodFailed(String name) {
+        throw new UnsupportedOperationException();
+    }
+
+    TestClassExecutionResult assertStdout(Matcher<? super String> matcher) {
+        def stdout = testClassNode.'system-out'[0].text();
+        assertThat(stdout, matcher)
+        this
+    }
+
+    TestClassExecutionResult assertStderr(Matcher<? super String> matcher) {
+        def stderr = testClassNode.'system-err'[0].text();
+        assertThat(stderr, matcher)
+        this
+    }
+
+    private def findTests() {
+        if (!checked) {
+            assertThat(testClassNode.name(), equalTo('testsuite'))
+            assertThat(testClassNode. at name.text(), equalTo(testClassName))
+            assertThat(testClassNode. at tests.text(), not(equalTo('')))
+            assertThat(testClassNode. at failures.text(), not(equalTo('')))
+            assertThat(testClassNode. at errors.text(), not(equalTo('')))
+            assertThat(testClassNode. at time.text(), not(equalTo('')))
+            assertThat(testClassNode. at timestamp.text(), not(equalTo('')))
+            assertThat(testClassNode. at hostname.text(), not(equalTo('')))
+            assertThat(testClassNode.properties.size(), equalTo(1))
+            testClassNode.testcase.each { node ->
+                assertThat(node. at classname.text(), equalTo(testClassName))
+                assertThat(node. at name.text(), not(equalTo('')))
+                assertThat(node. at time.text(), not(equalTo('')))
+                node.failure.each { failure ->
+                    assertThat(failure. at message.size(), equalTo(1))
+                    assertThat(failure. at type.text(), not(equalTo('')))
+                    assertThat(failure.text(), not(equalTo('')))
+                }
+            }
+            assertThat(testClassNode.'system-out'.size(), equalTo(1))
+            assertThat(testClassNode.'system-err'.size(), equalTo(1))
+            checked = true
+        }
+        Map testMethods = [:]
+        testClassNode.testcase.each { testMethods[it. at name.text()] = it }
+        return testMethods
+    }
+
+    private def findIgnoredTests() {
+        Map testMethods = [:]
+        testClassNode."ignored-testcase".each { testMethods[it. at name.text()] = it }
+        return testMethods
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/JavaProjectIntegrationTest.java b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/JavaProjectIntegrationTest.java
new file mode 100644
index 0000000..7f7cdc9
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/JavaProjectIntegrationTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.integtests.fixtures.ExecutionFailure;
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest;
+import org.gradle.util.TestFile;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class JavaProjectIntegrationTest extends AbstractIntegrationTest {
+    @Test
+    public void handlesEmptyProject() {
+        testFile("build.gradle").writelns("apply plugin: 'java'");
+        inTestDirectory().withTasks("build").run();
+    }
+
+    @Test
+    public void compilationFailureBreaksBuild() {
+        TestFile buildFile = testFile("build.gradle");
+        buildFile.writelns("apply plugin: 'java'");
+        testFile("src/main/java/org/gradle/broken.java").write("broken");
+
+        ExecutionFailure failure = usingBuildFile(buildFile).withTasks("build").runWithFailure();
+
+        failure.assertHasDescription("Execution failed for task ':compileJava'");
+        failure.assertHasCause("Compile failed; see the compiler error output for details.");
+    }
+
+    @Test
+    public void testCompilationFailureBreaksBuild() {
+        TestFile buildFile = testFile("build.gradle");
+        buildFile.writelns("apply plugin: 'java'");
+        testFile("src/main/java/org/gradle/ok.java").write("package org.gradle; class ok { }");
+        testFile("src/test/java/org/gradle/broken.java").write("broken");
+
+        ExecutionFailure failure = usingBuildFile(buildFile).withTasks("build").runWithFailure();
+
+        failure.assertHasDescription("Execution failed for task ':compileTestJava'");
+        failure.assertHasCause("Compile failed; see the compiler error output for details.");
+    }
+
+    @Test
+    public void handlesTestSrcWhichDoesNotContainAnyTestCases() {
+        TestFile buildFile = testFile("build.gradle");
+        buildFile.writelns("apply plugin: 'java'");
+        testFile("src/test/java/org/gradle/NotATest.java").writelns("package org.gradle;", "public class NotATest {}");
+
+        usingBuildFile(buildFile).withTasks("build").run();
+    }
+
+    @Test
+    public void javadocGenerationFailureBreaksBuild() throws IOException {
+        TestFile buildFile = testFile("javadocs.gradle");
+        buildFile.write("apply plugin: 'java'");
+        testFile("src/main/java/org/gradle/broken.java").write("class Broken { }");
+
+        ExecutionFailure failure = usingBuildFile(buildFile).withTasks("javadoc").runWithFailure();
+
+        failure.assertHasDescription("Execution failed for task ':javadoc'");
+        failure.assertHasCause("Javadoc generation failed.");
+    }
+
+    @Test
+    public void handlesResourceOnlyProject() throws IOException {
+        TestFile buildFile = testFile("resources.gradle");
+        buildFile.write("apply plugin: 'java'");
+        testFile("src/main/resources/org/gradle/resource.file").write("test resource");
+
+        usingBuildFile(buildFile).withTasks("build").run();
+        testFile("build/classes/main/org/gradle/resource.file").assertExists();
+    }
+
+    @Test
+    public void generatesArtifactsWhenVersionIsEmpty() {
+        testFile("settings.gradle").write("rootProject.name = 'empty'");
+        TestFile buildFile = testFile("build.gradle");
+        buildFile.writelns(
+                "apply plugin: 'java'",
+                "version = ''"
+        );
+        testFile("src/main/resources/org/gradle/resource.file").write("some resource");
+
+        usingBuildFile(buildFile).withTasks("jar").run();
+        testFile("build/libs/empty.jar").assertIsFile();
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/LoggingIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/LoggingIntegrationTest.groovy
new file mode 100644
index 0000000..85578c6
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/LoggingIntegrationTest.groovy
@@ -0,0 +1,362 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ExecutionResult
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.gradle.util.TestFile
+import org.junit.Rule
+import org.junit.Test
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.integtests.fixtures.UsesSample
+
+/**
+ * @author Hans Dockter
+ */
+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()
+
+    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',
+                'main 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',
+                ':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: evaluated project :project1',
+                'LOGGER: evaluated project :project2',
+                'LOGGER: executed task :project1:log',
+                'LOGGER: task :project1:log starting work',
+                'LOGGER: task :project1:log completed work',
+                'main 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'
+        )
+    }}
+
+    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")
+            }
+        }
+    }}
+
+    private final LogOutput brokenBuild = new LogOutput() {{
+        error('FAILURE: Could not determine which tasks to execute.')
+    }}
+
+    @Test
+    public void quietLogging() {
+        checkOutput(this.&run, logOutput.quiet)
+    }
+
+    @Test
+    public void lifecycleLogging() {
+        checkOutput(this.&run, logOutput.lifecycle)
+    }
+
+    @Test
+    public void infoLogging() {
+        checkOutput(this.&run, logOutput.info)
+    }
+
+    @Test
+    public void debugLogging() {
+        checkOutput(this.&run, logOutput.debug)
+    }
+
+    @Test
+    public void lifecycleLoggingForBrokenBuild() {
+        checkOutput(this.&runBroken, brokenBuild.lifecycle)
+    }
+
+    @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)
+    }
+
+    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()
+    }
+
+    def runBroken(LogLevel level) {
+        TestFile loggingDir = dist.testDir
+
+        return executer.inDirectory(loggingDir).withTasks('broken').runWithFailure()
+    }
+
+    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)
+        }
+        forbiddenMessages.each {List messages ->
+            checkOuts(false, result.output, messages) {expected, actual-> actual.contains(expected)}
+            checkOuts(false, result.error, messages) {expected, actual-> actual.contains(expected)}
+        }
+    }
+
+    def checkOuts(boolean shouldContain, String result, List outs, Closure partialLine) {
+        outs.each {String expectedOut ->
+            def found = result.readLines().findAll {partialLine.call(expectedOut, it)}
+            if (found.empty && shouldContain) {
+                throw new AssertionFailedError("Could not find expected line '$expectedOut' in output:\n$result")
+            }
+            if (!found.empty && !shouldContain) {
+                throw new AssertionFailedError("Found unexpected line '$expectedOut' in output:\n$result")
+            }
+            if (found.size() > 1) {
+                throw new AssertionFailedError("Found line '$expectedOut' multiple times in output:\n$result")
+            }
+        }
+    }
+}
+
+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/integ-test/src/integTest/groovy/org/gradle/integtests/MultiprojectIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/MultiprojectIntegrationTest.groovy
new file mode 100644
index 0000000..7ead401
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/MultiprojectIntegrationTest.groovy
@@ -0,0 +1,48 @@
+/*
+ * 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.junit.Test
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest
+
+class MultiProjectIntegrationTest extends AbstractIntegrationTest {
+    @Test
+    public void canInjectConfigurationFromParentProject() {
+        testFile('settings.gradle') << 'include "a", "b"'
+        testFile('build.gradle') << '''
+            allprojects {
+                def destDir = buildDir
+                task test << {
+                    destDir.mkdirs()
+                    new File(destDir, 'test.txt') << 'content'
+                }
+                gradle.taskGraph.whenReady {
+                    destDir.mkdirs()
+                    new File(destDir, 'whenReady.txt') << 'content'
+                }
+                afterEvaluate {
+                    destDir.mkdirs()
+                    new File(destDir, 'afterEvaluate.txt') << 'content'
+                }
+            }
+'''
+        inTestDirectory().withTasks('test').run()
+
+        testFile('build').assertHasDescendants('test.txt', 'whenReady.txt', 'afterEvaluate.txt')
+        testFile('a/build').assertHasDescendants('test.txt', 'whenReady.txt', 'afterEvaluate.txt')
+        testFile('b/build').assertHasDescendants('test.txt', 'whenReady.txt', 'afterEvaluate.txt')
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/OsgiProjectSampleIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/OsgiProjectSampleIntegrationTest.groovy
new file mode 100644
index 0000000..bb834ab
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/OsgiProjectSampleIntegrationTest.groovy
@@ -0,0 +1,59 @@
+/*
+ * 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 java.util.jar.Manifest
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.gradle.util.TestFile
+import org.junit.Rule
+import org.junit.Test
+import static org.junit.Assert.*
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.util.GradleVersion
+
+/**
+ * @author Hans Dockter
+ */
+class OsgiProjectSampleIntegrationTest {
+    @Rule public final GradleDistribution dist = new GradleDistribution()
+    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+    @Rule public final Sample sample = new Sample('osgi')
+
+    @Test
+    public void osgiProjectSamples() {
+        long start = System.currentTimeMillis()
+        TestFile osgiProjectDir = sample.dir
+        executer.inDirectory(osgiProjectDir).withTasks('clean', 'assemble').run()
+        TestFile tmpDir = dist.testDir
+        osgiProjectDir.file('build/libs/osgi-1.0.jar').unzipTo(tmpDir)
+        tmpDir.file('META-INF/MANIFEST.MF').withInputStream { InputStream instr ->
+            Manifest manifest = new Manifest(instr)
+            checkManifest(manifest, start)
+        }
+    }
+
+    static void checkManifest(Manifest manifest, start) {
+        assertEquals('Example Gradle Activator', manifest.mainAttributes.getValue('Bundle-Name'))
+        assertEquals('2', manifest.mainAttributes.getValue('Bundle-ManifestVersion'))
+        assertEquals('Bnd-1.15.0', manifest.mainAttributes.getValue('Tool'))
+        assertTrue(start <= Long.parseLong(manifest.mainAttributes.getValue('Bnd-LastModified')))
+        assertEquals('1.0', manifest.mainAttributes.getValue('Bundle-Version'))
+        assertEquals('gradle_tooling.osgi', manifest.mainAttributes.getValue('Bundle-SymbolicName'))
+        assertEquals( GradleVersion.current().version, manifest.mainAttributes.getValue('Built-By'))
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/ProjectLayoutIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ProjectLayoutIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/ProjectLayoutIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ProjectLayoutIntegrationTest.groovy
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ProjectLoadingIntegrationTest.java b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ProjectLoadingIntegrationTest.java
new file mode 100644
index 0000000..43896df
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ProjectLoadingIntegrationTest.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.integtests.fixtures.ExecutionFailure;
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest;
+import org.gradle.util.TestFile;
+import org.junit.Test;
+
+import java.io.File;
+
+import static org.hamcrest.Matchers.*;
+
+public class ProjectLoadingIntegrationTest extends AbstractIntegrationTest {
+    @Test
+    public void handlesSimilarlyNamedBuildFilesInSameDirectory() {
+        TestFile buildFile1 = testFile("similarly-named build.gradle").write("task build");
+        TestFile buildFile2 = testFile("similarly_named_build_gradle").write("task 'other-build'");
+
+        usingBuildFile(buildFile1).withTasks("build").run();
+
+        usingBuildFile(buildFile2).withTasks("other-build").run();
+
+        usingBuildFile(buildFile1).withTasks("build").run();
+    }
+
+    @Test
+    public void handlesWhitespaceOnlySettingsAndBuildFiles() {
+        testFile("settings.gradle").write("   \n  ");
+        testFile("build.gradle").write("   ");
+        inTestDirectory().withTaskList().run();
+    }
+
+    @Test
+    public void embeddedBuildFileIgnoresBuildAndScriptFiles() {
+        File rootDir = getTestDir();
+        testFile("settings.gradle").write("throw new RuntimeException()");
+        testFile("build.gradle").write("throw new RuntimeException()");
+        inDirectory(rootDir).usingBuildScript("Task task = task('do-stuff')").withTasks("do-stuff").run();
+    }
+
+    @Test
+    public void canDetermineRootProjectAndDefaultProjectBasedOnCurrentDirectory() {
+        File rootDir = getTestDir();
+        File childDir = new File(rootDir, "child");
+
+        testFile("settings.gradle").write("include('child')");
+        testFile("build.gradle").write("task('do-stuff')");
+        testFile("child/build.gradle").write("task('do-stuff')");
+
+        inDirectory(rootDir).withSearchUpwards().withTasks("do-stuff").run().assertTasksExecuted(":do-stuff", ":child:do-stuff");
+        inDirectory(rootDir).withSearchUpwards().withTasks(":do-stuff").run().assertTasksExecuted(":do-stuff");
+
+        inDirectory(childDir).withSearchUpwards().withTasks("do-stuff").run().assertTasksExecuted(":child:do-stuff");
+        inDirectory(childDir).withSearchUpwards().withTasks(":do-stuff").run().assertTasksExecuted(":do-stuff");
+    }
+
+    @Test
+    public void canDetermineRootProjectAndDefaultProjectBasedOnProjectDirectory() {
+        File rootDir = getTestDir();
+        File childDir = new File(rootDir, "child");
+
+        testFile("settings.gradle").write("include('child')");
+        testFile("build.gradle").write("task('do-stuff')");
+        testFile("child/build.gradle").write("task('do-stuff')");
+
+        usingProjectDir(rootDir).withSearchUpwards().withTasks("do-stuff").run().assertTasksExecuted(":do-stuff", ":child:do-stuff");
+        usingProjectDir(rootDir).withSearchUpwards().withTasks(":do-stuff").run().assertTasksExecuted(":do-stuff");
+
+        usingProjectDir(childDir).withSearchUpwards().withTasks("do-stuff").run().assertTasksExecuted(":child:do-stuff");
+        usingProjectDir(childDir).withSearchUpwards().withTasks(":do-stuff").run().assertTasksExecuted(":do-stuff");
+    }
+
+    @Test
+    public void canDetermineRootProjectAndDefaultProjectBasedOnBuildFile() {
+        testFile("settings.gradle").write("include('child')");
+
+        TestFile rootBuildFile = testFile("build.gradle");
+        rootBuildFile.write("task('do-stuff')");
+
+        TestFile childBuildFile = testFile("child/build.gradle");
+        childBuildFile.write("task('do-stuff')");
+
+        usingBuildFile(rootBuildFile).withSearchUpwards().withTasks("do-stuff").run().assertTasksExecuted(":do-stuff", ":child:do-stuff");
+        usingBuildFile(rootBuildFile).withSearchUpwards().withTasks(":do-stuff").run().assertTasksExecuted(":do-stuff");
+
+        usingBuildFile(childBuildFile).withSearchUpwards().withTasks("do-stuff").run().assertTasksExecuted(":child:do-stuff");
+        usingBuildFile(childBuildFile).withSearchUpwards().withTasks(":do-stuff").run().assertTasksExecuted(":do-stuff");
+    }
+
+    @Test
+    public void buildFailsWhenMultipleProjectsMeetDefaultProjectCriteria() {
+        testFile("settings.gradle").writelns(
+            "include 'child'",
+            "project(':child').projectDir = rootProject.projectDir");
+        testFile("build.gradle").write("// empty");
+
+        ExecutionFailure result = inTestDirectory().withTasks("test").runWithFailure();
+        result.assertThatDescription(startsWith("Could not select the default project for this build. Multiple projects in this build have project directory"));
+
+        result = usingProjectDir(getTestDir()).withTasks("test").runWithFailure();
+        result.assertThatDescription(startsWith("Could not select the default project for this build. Multiple projects in this build have project directory"));
+
+        result = usingBuildFile(testFile("build.gradle")).withTasks("test").runWithFailure();
+        result.assertThatDescription(startsWith("Could not select the default project for this build. Multiple projects in this build have build file"));
+    }
+
+    @Test
+    public void buildFailsWhenSpecifiedBuildFileIsNotAFile() {
+        ExecutionFailure result = usingBuildFile(testFile("unknown build file")).runWithFailure();
+        result.assertThatDescription(startsWith("Build file"));
+        result.assertThatDescription(endsWith("does not exist."));
+    }
+
+    @Test
+    public void buildFailsWhenSpecifiedProjectDirectoryIsNotADirectory() {
+        ExecutionFailure result = usingProjectDir(testFile("unknown dir")).runWithFailure();
+        result.assertThatDescription(startsWith("Project directory"));
+        result.assertThatDescription(endsWith("does not exist."));
+    }
+
+    @Test
+    public void buildFailsWhenSpecifiedSettingsFileIsNotAFile() {
+        ExecutionFailure result = inTestDirectory().usingSettingsFile(testFile("unknown")).runWithFailure();
+        result.assertThatDescription(startsWith("Could not read settings file"));
+        result.assertThatDescription(endsWith("as it does not exist."));
+    }
+
+    @Test
+    public void buildFailsWhenSpecifiedSettingsFileDoesNotContainMatchingProject() {
+        TestFile settingsFile = testFile("settings.gradle");
+        settingsFile.write("// empty");
+
+        TestFile projectdir = testFile("project dir");
+        projectdir.mkdirs();
+
+        ExecutionFailure result = usingProjectDir(projectdir).usingSettingsFile(settingsFile).runWithFailure();
+        result.assertThatDescription(startsWith("Could not select the default project for this build. No projects in this build have project directory"));
+    }
+
+    @Test
+    public void settingsFileTakesPrecedenceOverBuildFileInSameDirectory() {
+        testFile("settings.gradle").write("rootProject.buildFileName = 'root.gradle'");
+        testFile("root.gradle").write("task('do-stuff')");
+        
+        TestFile buildFile = testFile("build.gradle");
+        buildFile.write("throw new RuntimeException()");
+
+        inTestDirectory().withTasks("do-stuff").run();
+        usingProjectDir(getTestDir()).withTasks("do-stuff").run();
+    }
+
+    @Test
+    public void settingsFileInParentDirectoryTakesPrecedenceOverBuildFile() {
+        testFile("settings.gradle").writelns(
+            "include 'child'",
+            "project(':child').buildFileName = 'child.gradle'"
+        );
+
+        TestFile subDirectory = getTestDir().file("child");
+        subDirectory.file("build.gradle").write("throw new RuntimeException()");
+        subDirectory.file("child.gradle").write("task('do-stuff')");
+
+        inDirectory(subDirectory).withSearchUpwards().withTasks("do-stuff").run();
+        usingProjectDir(subDirectory).withSearchUpwards().withTasks("do-stuff").run();
+    }
+
+    @Test
+    public void explicitBuildFileTakesPrecedenceOverSettingsFileInSameDirectory() {
+        testFile("settings.gradle").write("rootProject.buildFileName = 'root.gradle'");
+        testFile("root.gradle").write("throw new RuntimeException()");
+
+        TestFile buildFile = testFile("build.gradle");
+        buildFile.write("task('do-stuff')");
+
+        usingBuildFile(buildFile).withTasks("do-stuff").run();
+    }
+
+    @Test
+    public void ignoresMultiProjectBuildInParentDirectoryWhichDoesNotMeetDefaultProjectCriteria() {
+        testFile("settings.gradle").write("include 'another'");
+        testFile("gradle.properties").writelns("prop=value2", "otherProp=value");
+
+        TestFile subDirectory = getTestDir().file("subdirectory");
+        TestFile buildFile = subDirectory.file("build.gradle");
+        buildFile.writelns("task('do-stuff') << {",
+                "assert prop == 'value'",
+                "assert !project.hasProperty('otherProp')",
+                "}");
+        testFile("subdirectory/gradle.properties").write("prop=value");
+
+        inDirectory(subDirectory).withSearchUpwards().withTasks("do-stuff").run();
+        usingProjectDir(subDirectory).withSearchUpwards().withTasks("do-stuff").run();
+        usingBuildFile(buildFile).withSearchUpwards().withTasks("do-stuff").run();
+    }
+
+    @Test
+    public void multiProjectBuildCanHaveMultipleProjectsWithSameProjectDir() {
+        testFile("settings.gradle").writelns(
+            "include 'child1', 'child2'",
+            "project(':child1').projectDir = new File(settingsDir, 'shared')",
+            "project(':child2').projectDir = new File(settingsDir, 'shared')"
+        );
+        testFile("shared/build.gradle").write("task('do-stuff')");
+
+        inTestDirectory().withTasks("do-stuff").run().assertTasksExecuted(":child1:do-stuff", ":child2:do-stuff");
+    }
+
+    @Test
+    public void multiProjectBuildCanHaveSeveralProjectsWithSameBuildFile() {
+        testFile("settings.gradle").writelns(
+            "include 'child1', 'child2'",
+            "project(':child1').buildFileName = '../child.gradle'",
+            "project(':child2').buildFileName = '../child.gradle'"
+        );
+        testFile("child.gradle").write("task('do-stuff')");
+
+        inTestDirectory().withTasks("do-stuff").run().assertTasksExecuted(":child1:do-stuff", ":child2:do-stuff");
+    }
+
+    @Test
+    public void multiProjectBuildCanHaveSettingsFileAndRootBuildFileInSubDir() {
+        TestFile buildFilesDir = getTestDir().file("root");
+        TestFile settingsFile = buildFilesDir.file("settings.gradle");
+        settingsFile.writelns(
+            "includeFlat 'child'",
+            "rootProject.projectDir = new File(settingsDir, '..')",
+            "rootProject.buildFileName = 'root/build.gradle'"
+        );
+
+        TestFile rootBuildFile = buildFilesDir.file("build.gradle");
+        rootBuildFile.write("task('do-stuff', dependsOn: ':child:task')");
+
+        TestFile childBuildFile = testFile("child/build.gradle");
+        childBuildFile.writelns("task('do-stuff')", "task('task')");
+
+        usingProjectDir(getTestDir()).usingSettingsFile(settingsFile).withTasks("do-stuff").run().assertTasksExecuted(":child:task", ":do-stuff", ":child:do-stuff");
+        usingBuildFile(rootBuildFile).withTasks("do-stuff").run().assertTasksExecuted(":child:task", ":do-stuff", ":child:do-stuff");
+        usingBuildFile(childBuildFile).usingSettingsFile(settingsFile).withTasks("do-stuff").run().assertTasksExecuted(":child:do-stuff");
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesAntlrIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesAntlrIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesAntlrIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesAntlrIntegrationTest.groovy
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesApplicationIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesApplicationIntegrationTest.groovy
new file mode 100644
index 0000000..f8d0442
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesApplicationIntegrationTest.groovy
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.util.TestFile
+import org.junit.Rule
+import spock.lang.Specification
+import org.gradle.integtests.fixtures.*
+
+class SamplesApplicationIntegrationTest extends Specification {
+    @Rule public final GradleDistribution distribution = new GradleDistribution()
+    @Rule public final GradleExecuter executer = new GradleDistributionExecuter()
+    @Rule public final Sample sample = new Sample('application')
+
+    def canRunTheApplicationUsingRunTask() {
+        when:
+        def result = executer.inDirectory(sample.dir).withTasks('run').run()
+
+        then:
+        result.output.contains('Greetings from the sample application.')
+    }
+
+    def canBuildAndRunTheInstalledApplication() {
+        when:
+        executer.inDirectory(sample.dir).withTasks('installApp').run()
+
+        then:
+        def installDir = sample.dir.file('build/install/application')
+        installDir.assertIsDir()
+
+        checkApplicationImage(installDir)
+    }
+
+    def canBuildAndRunTheZippedDistribution() {
+        when:
+        executer.inDirectory(sample.dir).withTasks('distZip').run()
+
+        then:
+        def distFile = sample.dir.file('build/distributions/application-1.0.2.zip')
+        distFile.assertIsFile()
+
+        def installDir = sample.dir.file('unzip')
+        distFile.usingNativeTools().unzipTo(installDir)
+
+        checkApplicationImage(installDir.file('application-1.0.2'))
+    }
+    
+    private void checkApplicationImage(TestFile installDir) {
+        installDir.file('bin/application').assertIsFile()
+        installDir.file('bin/application.bat').assertIsFile()
+        installDir.file('lib/application-1.0.2.jar').assertIsFile()
+        installDir.file('lib/commons-collections-3.2.1.jar').assertIsFile()
+
+        def builder = new ScriptExecuter()
+        builder.workingDir installDir.file('bin')
+        builder.executable 'application'
+        builder.standardOutput = new ByteArrayOutputStream()
+        builder.errorOutput = new ByteArrayOutputStream()
+
+        def result = builder.run()
+        result.assertNormalExitValue()
+
+        assert builder.standardOutput.toString().contains('Greetings from the sample application.')
+        assert builder.errorOutput.toString() == ''
+    }
+
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesCodeQualityIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesCodeQualityIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesCodeQualityIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesCodeQualityIntegrationTest.groovy
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesCustomBuildLanguageIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesCustomBuildLanguageIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesCustomBuildLanguageIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesCustomBuildLanguageIntegrationTest.groovy
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesCustomPluginIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesCustomPluginIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesCustomPluginIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesCustomPluginIntegrationTest.groovy
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesExcludesAndClassifiersIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesExcludesAndClassifiersIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesExcludesAndClassifiersIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesExcludesAndClassifiersIntegrationTest.groovy
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesGroovyCustomizedLayoutIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesGroovyCustomizedLayoutIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesGroovyCustomizedLayoutIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesGroovyCustomizedLayoutIntegrationTest.groovy
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesGroovyMultiProjectIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesGroovyMultiProjectIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesGroovyMultiProjectIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesGroovyMultiProjectIntegrationTest.groovy
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesGroovyOldVersionsIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesGroovyOldVersionsIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesGroovyOldVersionsIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesGroovyOldVersionsIntegrationTest.groovy
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesGroovyQuickstartIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesGroovyQuickstartIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesGroovyQuickstartIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesGroovyQuickstartIntegrationTest.groovy
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesIvyPublishIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesIvyPublishIntegrationTest.groovy
new file mode 100644
index 0000000..5267d91
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesIvyPublishIntegrationTest.groovy
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.gradle.integtests.fixtures.Sample
+import org.junit.Rule
+import org.junit.Test
+
+/**
+ * @author Hans Dockter
+ */
+public class SamplesIvyPublishIntegrationTest {
+    @Rule
+    public final GradleDistribution dist = new GradleDistribution()
+    @Rule
+    public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+    @Rule
+    public final Sample sample = new Sample("ivypublish")
+
+    @Test
+    public void testPublish() {
+        // the actual testing is done in the build script.
+        File projectDir = sample.dir
+        executer.inDirectory(projectDir).withTasks("uploadArchives").run()
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesJavaBaseIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesJavaBaseIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesJavaBaseIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesJavaBaseIntegrationTest.groovy
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesJavaCustomizedLayoutIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesJavaCustomizedLayoutIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesJavaCustomizedLayoutIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesJavaCustomizedLayoutIntegrationTest.groovy
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesJavaMultiProjectIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesJavaMultiProjectIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesJavaMultiProjectIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesJavaMultiProjectIntegrationTest.groovy
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesJavaOnlyIfIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesJavaOnlyIfIntegrationTest.groovy
new file mode 100644
index 0000000..a7c8dc4
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesJavaOnlyIfIntegrationTest.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.integtests
+
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.gradle.util.TestFile
+import org.junit.Rule
+import org.junit.Test
+import org.gradle.integtests.fixtures.Sample
+
+public class SamplesJavaOnlyIfIntegrationTest {
+    @Rule public final GradleDistribution dist = new GradleDistribution()
+    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+    @Rule public final Sample sample = new Sample('java/onlyif')
+
+    /**
+     * runs a build 3 times.
+     * execute clean dists
+     * check worked correctly
+     *
+     * remove test results
+     * execute dists
+     * check didn't re-run tests
+     *
+     * remove class file
+     * execute dists
+     * check that it re-ran tests 
+     */
+    @Test public void testOptimizedBuild() {
+        TestFile javaprojectDir = sample.dir
+
+        // Build and test projects
+        executer.inDirectory(javaprojectDir).withTasks('clean', 'build').run()
+
+        // Check tests have run
+        assertExists(javaprojectDir, 'build/test-results/TEST-org.gradle.PersonTest.xml')
+        assertExists(javaprojectDir, 'build/reports/tests/index.html')
+
+        // Check jar exists
+        assertExists(javaprojectDir, "build/libs/onlyif.jar")
+
+        // remove test results
+        removeFile(javaprojectDir, 'build/test-results/TEST-org.gradle.PersonTest.xml')
+        removeFile(javaprojectDir, 'build/reports/tests/index.html')
+
+        executer.inDirectory(javaprojectDir).withTasks('test').run()
+
+        // assert that tests did not run
+        // (since neither compile nor compileTests should have done anything)
+        assertDoesNotExist(javaprojectDir, 'build/test-results/TEST-org.gradle.PersonTest.xml')
+        assertDoesNotExist(javaprojectDir, 'build/reports/tests/index.html')
+
+        // remove a compiled class file
+        removeFile(javaprojectDir, 'build/classes/main/org/gradle/Person.class')
+
+        executer.inDirectory(javaprojectDir).withTasks('test').run()
+
+        // Check tests have run
+        assertExists(javaprojectDir, 'build/test-results/TEST-org.gradle.PersonTest.xml')
+        assertExists(javaprojectDir, 'build/reports/tests/index.html')
+    }
+
+    private static void assertExists(File baseDir, String path) {
+        new TestFile(baseDir).file(path).assertExists()
+    }
+
+    private static void assertDoesNotExist(File baseDir, String path) {
+        new TestFile(baseDir).file(path).assertDoesNotExist()
+    }
+
+    private static void removeFile(File baseDir, String path) {
+        TestFile file = new TestFile(baseDir).file(path)
+        file.assertExists()
+        file.delete()
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesJavaProjectWithIntTestsIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesJavaProjectWithIntTestsIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesJavaProjectWithIntTestsIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesJavaProjectWithIntTestsIntegrationTest.groovy
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesJavaQuickstartIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesJavaQuickstartIntegrationTest.groovy
new file mode 100644
index 0000000..fbf9352
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesJavaQuickstartIntegrationTest.groovy
@@ -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.integtests
+
+import java.util.jar.Manifest
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.gradle.util.TestFile
+import org.junit.Rule
+import org.junit.Test
+import static org.hamcrest.Matchers.*
+import static org.junit.Assert.*
+import org.gradle.integtests.fixtures.Sample
+
+/**
+ * @author Hans Dockter
+ */
+class SamplesJavaQuickstartIntegrationTest {
+    @Rule public final GradleDistribution dist = new GradleDistribution()
+    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+    @Rule public final Sample sample = new Sample('java/quickstart')
+
+    @Test
+    public void canBuildAndUploadJar() {
+        TestFile javaprojectDir = sample.dir
+
+        // Build and test projects
+        executer.inDirectory(javaprojectDir).withTasks('clean', 'build', 'uploadArchives').run()
+
+        // Check tests have run
+        JUnitTestExecutionResult result = new JUnitTestExecutionResult(javaprojectDir)
+        result.assertTestClassesExecuted('org.gradle.PersonTest')
+
+        // Check jar exists
+        javaprojectDir.file("build/libs/quickstart-1.0.jar").assertIsFile()
+
+        // Check jar uploaded
+        javaprojectDir.file('repos/quickstart-1.0.jar').assertIsFile()
+
+        // Check contents of Jar
+        TestFile jarContents = dist.testDir.file('jar')
+        javaprojectDir.file('repos/quickstart-1.0.jar').unzipTo(jarContents)
+        jarContents.assertHasDescendants(
+                'META-INF/MANIFEST.MF',
+                'org/gradle/Person.class',
+                'org/gradle/resource.xml'
+        )
+
+        // Check contents of manifest
+        Manifest manifest = new Manifest()
+        jarContents.file('META-INF/MANIFEST.MF').withInputStream { manifest.read(it) }
+        assertThat(manifest.mainAttributes.size(), equalTo(3))
+        assertThat(manifest.mainAttributes.getValue('Manifest-Version'), equalTo('1.0'))
+        assertThat(manifest.mainAttributes.getValue('Implementation-Title'), equalTo('Gradle Quickstart'))
+        assertThat(manifest.mainAttributes.getValue('Implementation-Version'), equalTo('1.0'))
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesMixedJavaAndGroovyIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesMixedJavaAndGroovyIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesMixedJavaAndGroovyIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesMixedJavaAndGroovyIntegrationTest.groovy
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesMixedJavaAndScalaIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesMixedJavaAndScalaIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesMixedJavaAndScalaIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesMixedJavaAndScalaIntegrationTest.groovy
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesRepositoriesIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesRepositoriesIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesRepositoriesIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesRepositoriesIntegrationTest.groovy
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesScalaCustomizedLayoutIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesScalaCustomizedLayoutIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesScalaCustomizedLayoutIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesScalaCustomizedLayoutIntegrationTest.groovy
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesScalaQuickstartIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesScalaQuickstartIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesScalaQuickstartIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesScalaQuickstartIntegrationTest.groovy
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesWebProjectIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesWebProjectIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesWebProjectIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesWebProjectIntegrationTest.groovy
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesWebQuickstartIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesWebQuickstartIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/SamplesWebQuickstartIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SamplesWebQuickstartIntegrationTest.groovy
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ScalaProjectIntegrationTest.java b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ScalaProjectIntegrationTest.java
new file mode 100644
index 0000000..ce7bac6
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ScalaProjectIntegrationTest.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.integtests;
+
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest;
+import org.junit.Test;
+
+public class ScalaProjectIntegrationTest extends AbstractIntegrationTest {
+    @Test
+    public void handlesEmptyProject() {
+        testFile("build.gradle").writelns(
+                "apply plugin: 'scala'"
+        );
+        inTestDirectory().withTasks("build").run();
+    }
+
+    @Test
+    public void handlesJavaSourceOnly() {
+        testFile("src/main/java/somepackage/SomeClass.java").writelns("public class SomeClass { }");
+        testFile("build.gradle").write("apply plugin: 'scala'");
+        testFile("settings.gradle").write("rootProject.name='javaOnly'");
+        inTestDirectory().withTasks("build").run();
+        testFile("build/libs/javaOnly.jar").assertExists();
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SettingsScriptErrorIntegrationTest.java b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SettingsScriptErrorIntegrationTest.java
new file mode 100644
index 0000000..a952b4e
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SettingsScriptErrorIntegrationTest.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.integtests;
+
+import org.gradle.integtests.fixtures.ExecutionFailure;
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest;
+import org.gradle.util.TestFile;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class SettingsScriptErrorIntegrationTest extends AbstractIntegrationTest {
+    @Test
+    public void reportsSettingsScriptEvaluationFailsWithRuntimeException() throws IOException {
+        TestFile buildFile = testFile("some build.gradle");
+        TestFile settingsFile = testFile("some settings.gradle");
+        settingsFile.writelns("", "", "throw new RuntimeException('<failure message>')");
+
+        ExecutionFailure failure = usingBuildFile(buildFile).usingSettingsFile(settingsFile).withTasks("do-stuff")
+                .runWithFailure();
+
+        failure.assertHasFileName(String.format("Settings file '%s'", settingsFile));
+        failure.assertHasLineNumber(3);
+        failure.assertHasDescription("A problem occurred evaluating settings 'reportsSettingsScriptEvaluationFailsWithRuntimeException");
+        failure.assertHasCause("<failure message>");
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SettingsScriptExecutionIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SettingsScriptExecutionIntegrationTest.groovy
new file mode 100644
index 0000000..ac96950
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SettingsScriptExecutionIntegrationTest.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
+
+import org.gradle.integtests.fixtures.ArtifactBuilder
+import org.gradle.integtests.fixtures.ExecutionResult
+import org.gradle.util.TestFile
+import org.junit.Test
+import static org.hamcrest.Matchers.*
+import static org.junit.Assert.*
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest
+
+class SettingsScriptExecutionIntegrationTest extends AbstractIntegrationTest {
+    @Test
+    public void executesSettingsScriptWithCorrectEnvironment() {
+        createExternalJar()
+        createBuildSrc()
+
+        testFile('settings.gradle') << '''
+buildscript {
+    dependencies { classpath files('repo/test-1.3.jar') }
+}
+new org.gradle.test.BuildClass()
+new BuildSrcClass();
+println 'quiet message'
+captureStandardOutput(LogLevel.ERROR)
+println 'error message'
+assert settings != null
+assert buildscript.classLoader == getClass().classLoader.parent
+assert buildscript.classLoader == Thread.currentThread().contextClassLoader
+assert gradle.scriptClassLoader.parent == buildscript.classLoader.parent.parent
+'''
+        testFile('build.gradle') << 'task doStuff'
+
+        ExecutionResult result = inTestDirectory().withTasks('doStuff').run()
+        assertThat(result.output, containsString('quiet message'))
+        assertThat(result.output, not(containsString('error message')))
+        assertThat(result.error, containsString('error message'))
+        assertThat(result.error, not(containsString('quiet message')))
+    }
+
+    private TestFile createBuildSrc() {
+        return testFile('buildSrc/src/main/java/BuildSrcClass.java') << '''
+            public class BuildSrcClass { }
+'''
+    }
+
+    private def createExternalJar() {
+        ArtifactBuilder builder = artifactBuilder();
+        builder.sourceFile('org/gradle/test/BuildClass.java') << '''
+            package org.gradle.test;
+            public class BuildClass { }
+'''
+        builder.buildJar(testFile("repo/test-1.3.jar"))
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SyncTaskIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SyncTaskIntegrationTest.groovy
new file mode 100644
index 0000000..ae1fe80
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SyncTaskIntegrationTest.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.integtests
+
+import org.junit.Test
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest
+
+class SyncTaskIntegrationTest extends AbstractIntegrationTest {
+    @Test
+    public void copiesFilesAndRemovesExtraFilesFromDestDir() {
+        testFile('source').create {
+            dir1 { file 'file1.txt' }
+            dir2 {
+                subdir { file 'file2.txt' }
+                file 'file3.txt'
+            }
+        }
+        testFile('dest').create {
+            file 'extra.txt'
+            extraDir { file 'extra.txt' }
+            dir1 {
+                file 'extra.txt'
+                extraDir { file 'extra.txt' }
+            }
+        }
+
+        testFile('build.gradle') << '''
+            task sync(type: Sync) {
+                into 'dest'
+                from 'source'
+            }
+'''
+
+        inTestDirectory().withTasks('sync').run()
+
+        testFile('dest').assertHasDescendants(
+                'dir1/file1.txt',
+                'dir2/subdir/file2.txt',
+                'dir2/file3.txt'
+        )
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/TaskAutoDependencyIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/TaskAutoDependencyIntegrationTest.groovy
new file mode 100644
index 0000000..904aa60
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/TaskAutoDependencyIntegrationTest.groovy
@@ -0,0 +1,72 @@
+/*
+ * 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.junit.Ignore
+import org.junit.Test
+import org.gradle.integtests.fixtures.internal.AbstractIntegrationTest
+
+class TaskAutoDependencyIntegrationTest extends AbstractIntegrationTest {
+    @Test
+    public void autoAddsInputFileCollectionAsADependency() {
+        // Include a configuration with transitive dep on a Jar and an unmanaged Jar.
+
+        testFile('settings.gradle') << 'include "a", "b"'
+        testFile('a/build.gradle') << '''
+configurations { compile }
+dependencies { compile project(path: ':b', configuration: 'archives') }
+
+task doStuff(type: InputTask) {
+    src = configurations.compile + fileTree('src/java')
+}
+
+class InputTask extends DefaultTask {
+    @InputFiles
+    def FileCollection src
+}
+'''
+        testFile('b/build.gradle') << '''
+apply plugin: 'base'
+task jar << {
+    file('b.jar').text = 'some jar'
+}
+
+task otherJar(type: Jar) {
+    destinationDir = buildDir
+}
+
+configurations { archives }
+dependencies { archives files('b.jar') { builtBy jar } }
+artifacts { archives otherJar }
+'''
+        inTestDirectory().withTasks('doStuff').run().assertTasksExecuted(':b:jar', ':b:otherJar', ':a:doStuff')
+    }
+
+    @Test @Ignore
+    public void addsDependenciesForInheritedConfiguration() {
+        fail()
+    }
+
+    @Test @Ignore
+    public void addsDependenciesForFileCollectionInSameProject() {
+        fail()
+    }
+    
+    @Test @Ignore
+    public void addsDependenciesForFileCollectionInProjectWithNoArtifacts() {
+        fail()
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/TaskDefinitionIntegrationTest.java b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/TaskDefinitionIntegrationTest.java
new file mode 100644
index 0000000..9a0db6c
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/TaskDefinitionIntegrationTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.integtests.fixtures.internal.AbstractIntegrationTest;
+import org.junit.Test;
+
+public class TaskDefinitionIntegrationTest extends AbstractIntegrationTest {
+    @Test
+    public void canDefineTasksUsingTaskKeywordAndIdentifier() {
+        testFile("build.gradle").writelns(
+                "task nothing",
+                "task withAction << { }",
+                "task emptyOptions()",
+                "task task",
+                "task withOptions(dependsOn: [nothing, withAction, emptyOptions, task])",
+                "task withOptionsAndAction(dependsOn: withOptions) << { }");
+        inTestDirectory().withTasks("withOptionsAndAction").run().assertTasksExecuted(":emptyOptions", ":nothing",
+                ":task", ":withAction", ":withOptions", ":withOptionsAndAction");
+    }
+
+    @Test
+    public void canDefineTasksUsingTaskKeywordAndGString() {
+        testFile("build.gradle").writelns(
+                "v = 'Task'",
+                "task \"nothing$v\"",
+                "task \"withAction$v\" << { }",
+                "task \"emptyOptions$v\"()",
+                "task \"withOptions$v\"(dependsOn: [nothingTask, withActionTask, emptyOptionsTask])",
+                "task \"withOptionsAndAction$v\"(dependsOn: withOptionsTask) << { }");
+        inTestDirectory().withTasks("withOptionsAndActionTask").run().assertTasksExecuted(":emptyOptionsTask",
+                ":nothingTask", ":withActionTask", ":withOptionsTask", ":withOptionsAndActionTask");
+    }
+
+    @Test
+    public void canDefineTasksUsingTaskKeywordAndString() {
+        testFile("build.gradle").writelns(
+                "task 'nothing'",
+                "task 'withAction' << { }",
+                "task 'emptyOptions'()",
+                "task 'withOptions'(dependsOn: [nothing, withAction, emptyOptions])",
+                "task 'withOptionsAndAction'(dependsOn: withOptions) << { }");
+        inTestDirectory().withTasks("withOptionsAndAction").run().assertTasksExecuted(":emptyOptions", ":nothing",
+                ":withAction", ":withOptions", ":withOptionsAndAction");
+    }
+
+    @Test
+    public void canDefineTasksInNestedBlocks() {
+        testFile("build.gradle").writelns(
+                "2.times { task \"dynamic$it\" << { } }",
+                "if (dynamic0) { task inBlock }",
+                "def task() { task inMethod }",
+                "task()", "def cl = { -> task inClosure }",
+                "cl()",
+                "task all(dependsOn: [dynamic0, dynamic1, inBlock, inMethod, inClosure])");
+        inTestDirectory().withTasks("all").run().assertTasksExecuted(":dynamic0", ":dynamic1", ":inBlock", ":inClosure",
+                ":inMethod", ":all");
+    }
+
+    @Test
+    public void canDefineTasksUsingTaskMethodExpression() {
+        testFile("build.gradle").writelns(
+                "a = 'a' == 'b' ? null: task(withAction) << { }",
+                "a = task(nothing)",
+                "a = task(emptyOptions())", "taskName = 'dynamic'",
+                "a = task(\"$taskName\") << { }",
+                "a = task('string')",
+                "a = task('stringWithAction') << { }",
+                "a = task('stringWithOptions', description: 'description')",
+                "a = task('stringWithOptionsAndAction', description: 'description') << { }",
+                "a = task(withOptions, description: 'description')",
+                "a = task(withOptionsAndAction, description: 'description') << { }",
+                "a = task(anotherWithAction).doFirst\n{}", "task all(dependsOn: tasks.all)");
+        inTestDirectory().withTasks("all").run().assertTasksExecuted(":anotherWithAction", ":dynamic", ":emptyOptions",
+                ":nothing", ":string", ":stringWithAction", ":stringWithOptions", ":stringWithOptionsAndAction",
+                ":withAction", ":withOptions", ":withOptionsAndAction", ":all");
+    }
+
+    @Test
+    public void canConfigureTasksWhenTheyAreDefined() {
+        testFile("build.gradle").writelns(
+                "task withDescription { description = 'value' }",
+                "task(asMethod)\n{ description = 'value' }",
+                "task asStatement(type: TestTask) { property = 'value' }",
+                "task \"dynamic\"(type: TestTask) { property = 'value' }",
+                "v = task(asExpression, type: TestTask) { property = 'value' }",
+                "task(postConfigure, type: TestTask).configure { property = 'value' }",
+                "[asStatement, dynamic, asExpression, postConfigure].each { ",
+                "    assert 'value' == it.property",
+                "}",
+                "[withDescription, asMethod].each {",
+                "    assert 'value' == it.description",
+                "}",
+                "task all(dependsOn: tasks.all)",
+                "class TestTask extends DefaultTask { String property }");
+        inTestDirectory().withTasks("all").run();
+    }
+
+    @Test
+    public void doesNotHideLocalMethodsAndVariables() {
+        testFile("build.gradle").writelns(
+                "String name = 'a'; task name",
+//                "taskNameVar = 'b'; task taskNameVar",
+                "def taskNameMethod(String name = 'c') { name } ",
+//                "task taskNameMethod",
+                "task taskNameMethod('d')",
+                "def addTaskMethod(String methodParam) { task methodParam }",
+                "addTaskMethod('e')",
+                "def addTaskWithClosure(String methodParam) { task(methodParam) { property = 'value' } }",
+                "addTaskWithClosure('f')",
+                "def addTaskWithMap(String methodParam) { task(methodParam, description: 'description') }",
+                "addTaskWithMap('g')",
+                "cl = { String taskNameParam -> task taskNameParam }",
+                "cl.call('h')",
+                "cl = { String taskNameParam -> task(taskNameParam) { property = 'value' } }",
+                "cl.call('i')",
+                "assert 'value' == f.property",
+                "assert 'value' == i.property",
+                "task all(dependsOn: tasks.all)");
+        inTestDirectory().withTasks("all").run().assertTasksExecuted(":a", ":d", ":e", ":f", ":g", ":h", ":i", ":all");
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/TaskErrorExecutionIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/TaskErrorExecutionIntegrationTest.groovy
new file mode 100644
index 0000000..13b82b1
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/TaskErrorExecutionIntegrationTest.groovy
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.integtests.fixtures.internal.AbstractIntegrationTest
+import org.gradle.integtests.fixtures.ExecutionFailure
+import org.gradle.util.TestFile
+import org.junit.Test
+
+class TaskErrorExecutionIntegrationTest extends AbstractIntegrationTest {
+    @Test
+    public void reportsTaskActionExecutionFailsWithError() {
+        TestFile buildFile = testFile("build.gradle")
+        buildFile.writelns(
+                "task('do-stuff').doFirst",
+                "{",
+                "1/0",
+                "}")
+        ExecutionFailure failure = usingBuildFile(buildFile).withTasks("do-stuff").runWithFailure()
+
+        failure.assertHasFileName(String.format("Build file '%s'", buildFile))
+        failure.assertHasLineNumber(3)
+        failure.assertHasDescription("Execution failed for task ':do-stuff'")
+        failure.assertHasCause("Division by zero")
+    }
+
+    @Test
+    public void reportsTaskActionExecutionFailsWithRuntimeException() {
+        File buildFile = testFile("build.gradle").writelns(
+                "task brokenClosure << {",
+                "    throw new RuntimeException('broken closure')",
+                "}")
+
+        ExecutionFailure failure = usingBuildFile(buildFile).withTasks("brokenClosure").runWithFailure()
+
+        failure.assertHasFileName(String.format("Build file '%s'", buildFile))
+        failure.assertHasLineNumber(2)
+        failure.assertHasDescription("Execution failed for task ':brokenClosure'")
+        failure.assertHasCause("broken closure")
+    }
+
+    @Test
+    public void reportsTaskActionExecutionFailsFromJavaWithRuntimeException() {
+        testFile("buildSrc/src/main/java/org/gradle/BrokenTask.java").writelns(
+                "package org.gradle;",
+                "import org.gradle.api.Action;",
+                "import org.gradle.api.DefaultTask;",
+                "import org.gradle.api.Task;",
+                "public class BrokenTask extends DefaultTask {",
+                "    public BrokenTask() {",
+                "        doFirst(new Action<Task>() {",
+                "            public void execute(Task task) {",
+                "                throw new RuntimeException(\"broken action\");",
+                "            }",
+                "        });",
+                "    }",
+                "}"
+        )
+        File buildFile = testFile("build.gradle")
+        buildFile.write("task brokenJavaTask(type: org.gradle.BrokenTask)")
+
+        ExecutionFailure failure = usingBuildFile(buildFile).withTasks("brokenJavaTask").runWithFailure()
+
+        failure.assertHasDescription("Execution failed for task ':brokenJavaTask'")
+        failure.assertHasCause("broken action")
+    }
+
+    @Test
+    public void reportsTaskInjectedByOtherProjectFailsWithRuntimeException() {
+        testFile("settings.gradle").write("include 'a', 'b'")
+        TestFile buildFile = testFile("b/build.gradle")
+        buildFile.writelns(
+                "project(':a') {",
+                "    task a << {",
+                "        throw new RuntimeException('broken')",
+                "    }",
+                "}")
+
+        ExecutionFailure failure = inTestDirectory().withTasks("a").runWithFailure()
+
+        failure.assertHasFileName(String.format("Build file '%s'", buildFile))
+        failure.assertHasLineNumber(3)
+        failure.assertHasDescription("Execution failed for task ':a:a")
+        failure.assertHasCause("broken")
+    }
+
+    @Test
+    public void reportsTaskValidationFailure() {
+        def buildFile = testFile('build.gradle')
+        buildFile << '''
+class CustomTask extends DefaultTask {
+    @InputFile File srcFile
+    @OutputFile File destFile
+}
+
+task custom(type: CustomTask)
+'''
+
+        ExecutionFailure failure = inTestDirectory().withTasks("custom").runWithFailure()
+
+        failure.assertHasDescription("Some problems were found with the configuration of task ':custom'.")
+        failure.assertHasCause("No value has been specified for property 'srcFile'.")
+        failure.assertHasCause("No value has been specified for property 'destFile'.")
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/TaskExecutionIntegrationTest.java b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/TaskExecutionIntegrationTest.java
new file mode 100644
index 0000000..8360373
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/TaskExecutionIntegrationTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.integtests.fixtures.internal.AbstractIntegrationTest;
+import org.gradle.util.TestFile;
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.*;
+
+public class TaskExecutionIntegrationTest extends AbstractIntegrationTest {
+    @Test
+    public void taskCanAccessTaskGraph() {
+        TestFile buildFile = testFile("build.gradle");
+        buildFile.writelns(
+                "boolean notified = false",
+                "task a(dependsOn: 'b') << { task ->",
+                "    assert notified",
+                "    assert gradle.taskGraph.hasTask(task)",
+                "    assert gradle.taskGraph.hasTask(':a')",
+                "    assert gradle.taskGraph.hasTask(a)",
+                "    assert gradle.taskGraph.hasTask(':b')",
+                "    assert gradle.taskGraph.hasTask(b)",
+                "    assert gradle.taskGraph.allTasks.contains(task)",
+                "    assert gradle.taskGraph.allTasks.contains(tasks.getByName('b'))",
+                "}",
+                "task b",
+                "gradle.taskGraph.whenReady { graph ->",
+                "    assert graph.hasTask(':a')",
+                "    assert graph.hasTask(a)",
+                "    assert graph.hasTask(':b')",
+                "    assert graph.hasTask(b)",
+                "    assert graph.allTasks.contains(a)",
+                "    assert graph.allTasks.contains(b)",
+                "    notified = true",
+                "}");
+        usingBuildFile(buildFile).withTasks("a").run().assertTasksExecuted(":b", ":a");
+    }
+
+    @Test
+    public void executesAllTasksInASingleBuildAndEachTaskAtMostOnce() {
+        TestFile buildFile = testFile("build.gradle");
+        buildFile.writelns(
+                "gradle.taskGraph.whenReady { assert !project.hasProperty('graphReady'); graphReady = true }",
+                "task a << { task -> project.executedA = task }",
+                "task b << { ",
+                "    assert a == project.executedA",
+                "    assert gradle.taskGraph.hasTask(':a')",
+                "}",
+                "task c(dependsOn: a)",
+                "task d(dependsOn: a)",
+                "task e(dependsOn: [a, d])");
+        usingBuildFile(buildFile).withTasks("a", "b").run().assertTasksExecuted(":a", ":b");
+        usingBuildFile(buildFile).withTasks("a", "a").run().assertTasksExecuted(":a");
+        usingBuildFile(buildFile).withTasks("c", "a").run().assertTasksExecuted(":a", ":c");
+        usingBuildFile(buildFile).withTasks("c", "e").run().assertTasksExecuted(":a", ":c", ":d", ":e");
+    }
+
+    @Test
+    public void executesMultiProjectsTasksInASingleBuildAndEachTaskAtMostOnce() {
+        testFile("settings.gradle").writelns("include 'child1', 'child2'");
+        TestFile buildFile = testFile("build.gradle");
+        buildFile.writelns(
+                "task a",
+                "allprojects {",
+                "    task b",
+                "    task c(dependsOn: ['b', ':a'])",
+                "}");
+        usingBuildFile(buildFile).withTasks("a", "c").run().assertTasksExecuted(":a", ":b", ":c", ":child1:b",
+                ":child1:c", ":child2:b", ":child2:c");
+        usingBuildFile(buildFile).withTasks("b", ":child2:c").run().assertTasksExecuted(":b", ":child1:b", ":child2:b",
+                ":a", ":child2:c");
+    }
+
+    @Test
+    public void executesMultiProjectDefaultTasksInASingleBuildAndEachTaskAtMostOnce() {
+        testFile("settings.gradle").writelns("include 'child1', 'child2'");
+        TestFile buildFile = testFile("build.gradle");
+        buildFile.writelns("defaultTasks 'a', 'b'", "task a", "subprojects {", "    task a(dependsOn: ':a')",
+                "    task b(dependsOn: ':a')", "}");
+        usingBuildFile(buildFile).run().assertTasksExecuted(":a", ":child1:a", ":child2:a", ":child1:b", ":child2:b");
+    }
+
+    @Test
+    public void executesProjectDefaultTasksWhenNoneSpecified() {
+        TestFile buildFile = testFile("build.gradle");
+        buildFile.writelns(
+                "task a",
+                "task b(dependsOn: a)",
+                "defaultTasks 'b'"
+        );
+        usingBuildFile(buildFile).withTasks().run().assertTasksExecuted(":a", ":b");
+    }
+    
+    @Test
+    public void doesNotExecuteTaskActionsWhenDryRunSpecified() {
+        TestFile buildFile = testFile("build.gradle");
+        buildFile.writelns(
+                "task a << { fail() }",
+                "task b(dependsOn: a) << { fail() }",
+                "defaultTasks 'b'"
+        );
+
+        // project defaults
+        usingBuildFile(buildFile).withArguments("-m").run().assertTasksExecuted(":a", ":b");
+        // named tasks
+        usingBuildFile(buildFile).withArguments("-m").withTasks("b").run().assertTasksExecuted(":a", ":b");
+    }
+
+    @Test
+    public void excludesTasksWhenExcludePatternSpecified() {
+        testFile("settings.gradle").write("include 'sub'");
+        TestFile buildFile = testFile("build.gradle");
+        buildFile.writelns(
+                "task a",
+                "task b(dependsOn: a)",
+                "task c(dependsOn: [a, b])",
+                "task d(dependsOn: c)",
+                "defaultTasks 'd'"
+        );
+        testFile("sub/build.gradle").writelns(
+                "task c",
+                "task d(dependsOn: c)"
+        );
+
+        // Exclude entire branch
+        usingBuildFile(buildFile).withTasks(":d").withArguments("-x", "c").run().assertTasksExecuted(":d");
+        // Exclude direct dependency
+        usingBuildFile(buildFile).withTasks(":d").withArguments("-x", "b").run().assertTasksExecuted(":a", ":c", ":d");
+        // Exclude using paths and multi-project
+        usingBuildFile(buildFile).withTasks("d").withArguments("-x", "c").run().assertTasksExecuted(":d", ":sub:d");
+        usingBuildFile(buildFile).withTasks("d").withArguments("-x", "sub:c").run().assertTasksExecuted(":a", ":b", ":c", ":d", ":sub:d");
+        usingBuildFile(buildFile).withTasks("d").withArguments("-x", ":sub:c").run().assertTasksExecuted(":a", ":b", ":c", ":d", ":sub:d");
+        usingBuildFile(buildFile).withTasks("d").withArguments("-x", "d").run().assertTasksExecuted();
+        // Project defaults
+        usingBuildFile(buildFile).withArguments("-x", "b").run().assertTasksExecuted(":a", ":c", ":d", ":sub:c", ":sub:d");
+        // Unknown task
+        usingBuildFile(buildFile).withTasks("d").withArguments("-x", "unknown").runWithFailure().assertThatDescription(startsWith("Task 'unknown' not found in root project"));
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/UserGuideSamplesIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/UserGuideSamplesIntegrationTest.groovy
new file mode 100644
index 0000000..7bd237a
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/UserGuideSamplesIntegrationTest.groovy
@@ -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.integtests
+
+import org.junit.runner.RunWith
+
+ at RunWith(UserGuideSamplesRunner.class)
+class UserGuideSamplesIntegrationTest {
+    /*
+
+    Important info:
+
+     If you're working in samples area there're gradle tasks that you should know of:
+     - gradle intTestImage makes sure that the samples' resources are copied to the right place
+     - gradle docs (or one of its dependent tasks) makes sure that samples' info is extracted from XMLs
+
+    */
+}
\ No newline at end of file
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/UserGuideSamplesRunner.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/UserGuideSamplesRunner.groovy
new file mode 100644
index 0000000..cfd303d
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/UserGuideSamplesRunner.groovy
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 com.google.common.collect.ArrayListMultimap
+import com.google.common.collect.ListMultimap
+import groovy.io.PlatformLineWriter
+import junit.framework.AssertionFailedError
+import org.apache.tools.ant.taskdefs.Delete
+import org.gradle.integtests.fixtures.ExecutionResult
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.gradle.util.AntUtil
+import org.gradle.util.SystemProperties
+import org.junit.Assert
+import org.junit.runner.Description
+import org.junit.runner.Runner
+import org.junit.runner.notification.Failure
+import org.junit.runner.notification.RunNotifier
+
+class UserGuideSamplesRunner extends Runner {
+    private static final String NL = SystemProperties.lineSeparator
+
+    Class<?> testClass
+    Description description
+    Map<Description, SampleRun> samples;
+    GradleDistribution dist = new GradleDistribution()
+    GradleDistributionExecuter executer = new GradleDistributionExecuter(dist)
+
+    def UserGuideSamplesRunner(Class<?> testClass) {
+        this.testClass = testClass
+        this.description = Description.createSuiteDescription(testClass)
+        samples = new LinkedHashMap()
+        for (sample in getScriptsForSamples(dist.userGuideInfoDir)) {
+            Description childDescription = Description.createTestDescription(testClass, sample.id)
+            description.addChild(childDescription)
+            samples.put(childDescription, sample)
+
+            println "Sample $sample.id dir: $sample.subDir"
+            sample.runs.each { println "    args: $it.args expect: $it.outputFile" }
+        }
+    }
+
+    Description getDescription() {
+        return description
+    }
+
+    void run(RunNotifier notifier) {
+        for (childDescription in description.children) {
+            notifier.fireTestStarted(childDescription)
+            SampleRun sampleRun = samples.get(childDescription)
+            try {
+                cleanup(sampleRun)
+                for (run in sampleRun.runs) {
+                    runSample(run)
+                }
+            } catch (Throwable t) {
+                notifier.fireTestFailure(new Failure(childDescription, t))
+            }
+            notifier.fireTestFinished(childDescription)
+        }
+    }
+
+    private def cleanup(SampleRun run) {
+        // Clean up previous runs
+        File rootProjectDir = dist.samplesDir.file(run.subDir)
+        if (rootProjectDir.exists()) {
+            def delete = new Delete()
+            delete.dir = rootProjectDir
+            delete.includes = "**/.gradle/** **/build/**"
+            AntUtil.execute(delete)
+        }
+    }
+
+    private def runSample(GradleRun run) {
+        try {
+            println("Test Id: $run.id, dir: $run.subDir, args: $run.args")
+            File rootProjectDir = dist.samplesDir.file(run.subDir)
+            executer.inDirectory(rootProjectDir).withArguments(run.args as String[]).withEnvironmentVars(run.envs)
+
+            ExecutionResult result = run.expectFailure ? executer.runWithFailure() : executer.run()
+            if (run.outputFile) {
+                String expectedResult = replaceWithPlatformNewLines(dist.userGuideOutputDir.file(run.outputFile).text)
+                try {
+                    compareStrings(expectedResult, result.output, run.ignoreExtraLines)
+                } catch (AssertionFailedError e) {
+                    println 'Expected Result:'
+                    println expectedResult
+                    println 'Actual Result:'
+                    println result.output
+                    println '---'
+                    throw e
+                }
+            }
+
+            run.files.each { path ->
+                println "  checking file '$path' exists"
+                File file = new File(rootProjectDir, path).canonicalFile
+                Assert.assertTrue("Expected file '$file' does not exist.", file.exists())
+                Assert.assertTrue("Expected file '$file' is not a file.", file.isFile())
+            }
+            run.dirs.each { path ->
+                println "  checking directory '$path' exists"
+                File file = new File(rootProjectDir, path).canonicalFile
+                Assert.assertTrue("Expected directory '$file' does not exist.", file.exists())
+                Assert.assertTrue("Expected directory '$file' is not a directory.", file.isDirectory())
+            }
+        } catch (Throwable e) {
+            throw new AssertionError("Integration test for sample '$run.id' in dir '$run.subDir' with args $run.args failed:${NL}$e.message").initCause(e)
+        }
+    }
+
+    private def compareStrings(String expected, String actual, boolean ignoreExtraLines) {
+        List actualLines = normaliseOutput(actual.readLines())
+        List expectedLines = expected.readLines()
+        int pos = 0
+        for (; pos < actualLines.size() && pos < expectedLines.size(); pos++) {
+            String expectedLine = expectedLines[pos]
+            String actualLine = actualLines[pos]
+            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}---")
+                }
+                if (actualLine.contains(expectedLine)) {
+                    Assert.fail("Extra text at line ${pos + 1}.${NL}Expected: ${expectedLine}${NL}Actual: ${actualLine}${NL}---${NL}Actual output:${NL}$actual${NL}---")
+                }
+                Assert.fail("Unexpected value at line ${pos + 1}.${NL}Expected: ${expectedLine}${NL}Actual: ${actualLine}${NL}---${NL}Actual output:${NL}$actual${NL}---")
+            }
+        }
+        if (pos == actualLines.size() && pos < expectedLines.size()) {
+            Assert.fail("Lines missing from actual result, starting at line ${pos + 1}.${NL}Expected: ${expectedLines[pos]}${NL}Actual output:${NL}$actual${NL}---")
+        }
+        if (!ignoreExtraLines && pos < actualLines.size() && pos == expectedLines.size()) {
+            Assert.fail("Extra lines in actual result, starting at line ${pos + 1}.${NL}Actual: ${actualLines[pos]}${NL}Actual output:${NL}$actual${NL}---")
+        }
+    }
+
+    static String replaceWithPlatformNewLines(String text) {
+        StringWriter stringWriter = new StringWriter()
+        new PlatformLineWriter(stringWriter).withWriter { it.write(text) }
+        stringWriter.toString()
+    }
+
+    List<String> normaliseOutput(List<String> lines) {
+        lines.inject(new ArrayList<String>()) { List values, String line ->
+            if (line.matches('Download .+')) {
+                // ignore
+            } else {
+                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 Collection<SampleRun> getScriptsForSamples(File userguideInfoDir) {
+        def samplesXml = new File(userguideInfoDir, 'samples.xml')
+        assertSamplesGenerated(samplesXml.exists())
+        Node samples = new XmlParser().parse(samplesXml)
+        ListMultimap<String, GradleRun> samplesByDir = ArrayListMultimap.create()
+
+        def children = samples.children()
+        assertSamplesGenerated(!children.isEmpty())
+
+        children.each {Node sample ->
+            String id = sample.'@id'
+            String dir = sample.'@dir'
+            String args = sample.'@args'
+            String outputFile = sample.'@outputFile'
+            boolean ignoreExtraLines = Boolean.valueOf(sample.'@ignoreExtraLines')
+
+            GradleRun run = new GradleRun(id: id)
+            run.subDir = dir
+            run.args = args ? args.split('\\s+') as List : []
+            run.outputFile = outputFile
+            run.ignoreExtraLines = ignoreExtraLines as boolean
+
+            sample.file.each { file -> run.files << file.'@path' }
+            sample.dir.each { file -> run.dirs << file.'@path' }
+
+            samplesByDir.put(dir, run)
+        }
+
+        // Some custom values
+        samplesByDir.get('userguide/tutorial/properties').each { it.envs['ORG_GRADLE_PROJECT_envProjectProp'] = 'envPropertyValue' }
+        samplesByDir.get('userguide/buildlifecycle/taskExecutionEvents')*.expectFailure = true
+        samplesByDir.get('userguide/buildlifecycle/buildProjectEvaluateEvents')*.expectFailure = true
+
+        Map<String, SampleRun> samplesById = new TreeMap<String, SampleRun>()
+
+        // Remove duplicates for a given directory.
+        samplesByDir.asMap().values().collect {List<GradleRun> dirSamples ->
+            Collection<GradleRun> runs = dirSamples.findAll {it.mustRun}
+            if (!runs) {
+                // No samples in this dir have any args, so just run gradle tasks in the dir
+                def sample = dirSamples[0]
+                sample.args = ['tasks']
+                sample
+            } else {
+                return runs
+            }
+        }.flatten().each { GradleRun run ->
+            // Collect up into sample runs
+            SampleRun sampleRun = samplesById[run.id]
+            if (!sampleRun) {
+                sampleRun = new SampleRun(id: run.id, subDir: run.subDir)
+                samplesById[run.id] = sampleRun
+            }
+            sampleRun.runs << run
+        }
+
+        return samplesById.values()
+    }
+
+    static void assertSamplesGenerated(boolean assertion) {
+        assert assertion : """Couldn't find any samples. Most likely, samples.xml was not generated.
+Please run 'gradle check devBuild' first (you can skip tests in this case)
+Probably some other task can help you as well, please try: 'gradle docs intTestImage'"""
+    }
+}
+
+class SampleRun {
+    String id
+    String subDir
+    List<GradleRun> runs = []
+}
+
+class GradleRun {
+    String id
+    List args = []
+    String subDir
+    Map envs = [:]
+    String outputFile
+    boolean expectFailure
+    boolean ignoreExtraLines
+    List files = []
+    List dirs = []
+
+    boolean getMustRun() {
+        return args || files || dirs
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/WaterProjectIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/WaterProjectIntegrationTest.groovy
new file mode 100644
index 0000000..26fee4b
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/WaterProjectIntegrationTest.groovy
@@ -0,0 +1,79 @@
+/*
+ * 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.integtests
+
+import org.gradle.integtests.fixtures.ExecutionResult
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.util.SystemProperties
+import org.junit.Rule
+import org.junit.Test
+
+import static org.junit.Assert.*
+
+/**
+ * @author Hans Dockter
+ */
+class WaterProjectIntegrationTest {
+    final static String NL = SystemProperties.lineSeparator
+
+    final static String HELLO_CLAUSE = "Hello, I'm "
+    final static String CHILDREN_TEXT = 'I love water.'
+    final static String WATER_INFO = 'As you all know, I cover three quarters of this planet!'
+    final static String BLUE_WHALE_INFO = "I'm the largets animal which has ever lived on this planet!"
+    final static String KRILL_INFO = "The weight of my species in summer is twice as heavy as all human beings!"
+    final static String PHYTOPLANKTON_INFO = "I produce as much oxygen as all the other plants on earth together!"
+
+    final static String WATER_NAME = 'water'
+    final static String BLUE_WHALE_NAME = 'bluewhale'
+    final static String KRILL_NAME = 'krill'
+    final static String PHYTOPLANKTON_NAME = 'phytoplankton'
+
+    @Rule public final GradleDistribution dist = new GradleDistribution()
+    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+    @Rule public final Sample sample = new Sample(WATER_NAME)
+
+    @Test
+    public void waterProject() {
+        File waterDir = sample.dir
+        ExecutionResult result = executer.inDirectory(waterDir).withTasks('hello').withQuietLogging().run()
+        assertEquals(result.output, list2text([intro(WATER_NAME), WATER_INFO,
+                intro(PHYTOPLANKTON_NAME), CHILDREN_TEXT, PHYTOPLANKTON_INFO,
+                intro(KRILL_NAME), CHILDREN_TEXT, KRILL_INFO,
+                intro(BLUE_WHALE_NAME), CHILDREN_TEXT, BLUE_WHALE_INFO]))
+
+        result = executer.inDirectory(new File(waterDir, BLUE_WHALE_NAME)).withTasks('hello').withQuietLogging().run()
+        assertEquals(result.output, list2text([intro(WATER_NAME), WATER_INFO,
+                intro(PHYTOPLANKTON_NAME), CHILDREN_TEXT, PHYTOPLANKTON_INFO,
+                intro(KRILL_NAME), CHILDREN_TEXT, KRILL_INFO,
+                intro(BLUE_WHALE_NAME), CHILDREN_TEXT, BLUE_WHALE_INFO]))
+
+        result = executer.inDirectory(new File(waterDir, PHYTOPLANKTON_NAME)).withTasks('hello').withQuietLogging().run()
+        assertEquals(result.output, list2text([intro(WATER_NAME), WATER_INFO,
+                intro(PHYTOPLANKTON_NAME), CHILDREN_TEXT, PHYTOPLANKTON_INFO]))
+    }
+
+    static String intro(String projectName) {
+        HELLO_CLAUSE + projectName
+    }
+
+    static String list2text(List list) {
+        list.join(NL) + NL
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/WebProjectIntegrationTest.java b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/WebProjectIntegrationTest.java
new file mode 100644
index 0000000..f438505
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/WebProjectIntegrationTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.integtests.fixtures.internal.AbstractIntegrationTest;
+import org.gradle.util.TestFile;
+import org.junit.Test;
+
+public class WebProjectIntegrationTest extends AbstractIntegrationTest {
+    @Test
+    public void handlesEmptyProject() {
+        TestFile buildFile = testFile("build.gradle");
+        buildFile.writelns(
+                "apply plugin: 'war'"
+        );
+
+        usingBuildFile(buildFile).withTasks("build").run();
+    }
+
+    @Test
+    public void createsAWar() {
+        testFile("settings.gradle").writelns("rootProject.name = 'test'");
+        TestFile buildFile = testFile("build.gradle");
+        buildFile.writelns(
+                "apply plugin: 'war'"
+        );
+        testFile("src/main/webapp/index.jsp").write("<p>hi</p>");
+
+        usingBuildFile(buildFile).withTasks("assemble").run();
+        testFile("build/libs/test.war").assertIsFile();
+    }
+
+    @Test
+    public void canCustomiseArchiveNamesUsingConventionProperties() {
+        testFile("settings.gradle").writelns("rootProject.name = 'test'");
+        TestFile buildFile = testFile("build.gradle");
+        buildFile.writelns(
+                "apply plugin: 'war'",
+                "jar.enabled = true",
+                "buildDirName = 'output'",
+                "libsDirName = 'archives'",
+                "archivesBaseName = 'test'",
+                "version = '0.5-RC2'"
+        );
+        testFile("src/main/resources/org/gradle/resource.file").write("some resource");
+
+        usingBuildFile(buildFile).withTasks("assemble").run();
+        testFile("output/archives/test-0.5-RC2.jar").assertIsFile();
+        testFile("output/archives/test-0.5-RC2.war").assertIsFile();
+    }
+
+    @Test
+    public void generatesArtifactsWhenVersionIsEmpty() {
+        testFile("settings.gradle").write("rootProject.name = 'empty'");
+        TestFile buildFile = testFile("build.gradle");
+        buildFile.writelns(
+                "apply plugin: 'war'",
+                "jar.enabled = true",
+                "version = ''"
+        );
+        testFile("src/main/resources/org/gradle/resource.file").write("some resource");
+
+        usingBuildFile(buildFile).withTasks("assemble").run();
+        testFile("build/libs/empty.jar").assertIsFile();
+        testFile("build/libs/empty.war").assertIsFile();
+    }
+
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/WorkerProcessIntegrationTest.java b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/WorkerProcessIntegrationTest.java
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/WorkerProcessIntegrationTest.java
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/WorkerProcessIntegrationTest.java
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/WrapperProjectIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/WrapperProjectIntegrationTest.groovy
new file mode 100644
index 0000000..07c963a
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/WrapperProjectIntegrationTest.groovy
@@ -0,0 +1,56 @@
+/*
+ * 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.integtests
+
+import org.gradle.integtests.fixtures.ExecutionResult
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.junit.Rule
+import org.junit.Test
+import static org.hamcrest.Matchers.*
+import static org.junit.Assert.*
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.integtests.fixtures.ExecutionFailure
+
+/**
+ * @author Hans Dockter
+ */
+class WrapperProjectIntegrationTest {
+    @Rule public final GradleDistribution dist = new GradleDistribution()
+    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+    @Rule public final Sample sample = new Sample('wrapper-project')
+
+    @Test
+    public void hasNonZeroExitCodeOnBuildFailure() {
+        File wrapperSampleDir = sample.dir
+
+        executer.inDirectory(wrapperSampleDir).withTasks('wrapper').run()
+
+        ExecutionFailure failure = executer.usingExecutable('gradlew').inDirectory(wrapperSampleDir).withTasks('unknown').runWithFailure()
+        failure.assertHasDescription("Task 'unknown' not found in root project 'wrapper-project'.")
+    }
+
+    @Test
+    public void wrapperSample() {
+        File wrapperSampleDir = sample.dir
+
+        executer.inDirectory(wrapperSampleDir).withTasks('wrapper').run()
+
+        ExecutionResult result = executer.usingExecutable('gradlew').inDirectory(wrapperSampleDir).withTasks('hello').run()
+        assertThat(result.output, containsString('hello'))
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/maven/MavenProjectIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/maven/MavenProjectIntegrationTest.groovy
new file mode 100644
index 0000000..6ae64fd
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/maven/MavenProjectIntegrationTest.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.integtests.maven
+
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.junit.Rule
+import org.junit.Test
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.integtests.fixtures.MavenRepository
+
+class MavenProjectIntegrationTest {
+    @Rule public final GradleDistribution dist = new GradleDistribution()
+    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+    @Rule public final TestResources testResources = new TestResources()
+
+    @Test
+    public void handlesSubProjectsWithoutTheMavenPluginApplied() {
+        dist.testFile("settings.gradle").write("include 'subProject'");
+        dist.testFile("build.gradle") << '''
+            apply plugin: 'java'
+            apply plugin: 'maven'
+        '''
+        executer.withTaskList().run();
+    }
+
+    @Test
+    public void canDeployAProjectWithDependencyInMappedAndUnMappedConfiguration() {
+        executer.withTasks('uploadArchives').run()
+        def module = repo().module('group', 'root', 1.0)
+        module.assertArtifactsDeployed('root-1.0.jar')
+    }
+
+    @Test
+    public void canDeployAProjectWithNoMainArtifact() {
+        executer.withTasks('uploadArchives').run()
+        def module = repo().module('group', 'root', 1.0)
+        module.assertArtifactsDeployed('root-1.0-source.jar')
+    }
+
+    @Test
+    public void canDeployAProjectWithMetadataArtifacts() {
+        executer.withTasks('uploadArchives').run()
+        def module = repo().module('group', 'root', 1.0)
+        module.assertArtifactsDeployed('root-1.0.jar', 'root-1.0.jar.sig', 'root-1.0.pom', 'root-1.0.pom.sig')
+    }
+
+    def MavenRepository repo() {
+        new MavenRepository(dist.testFile('mavenRepo'))
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/maven/MavenRepoIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/maven/MavenRepoIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/maven/MavenRepoIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/maven/MavenRepoIntegrationTest.groovy
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/maven/MavenSnapshotIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/maven/MavenSnapshotIntegrationTest.groovy
new file mode 100644
index 0000000..ba8a6fe
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/maven/MavenSnapshotIntegrationTest.groovy
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.maven
+
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.gradle.integtests.fixtures.HttpServer
+import org.gradle.integtests.fixtures.TestResources
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+/**
+ * @author Hans Dockter
+ */
+class MavenSnapshotIntegrationTest {
+    @Rule public GradleDistribution distribution = new GradleDistribution()
+    @Rule public GradleDistributionExecuter executer = new GradleDistributionExecuter()
+    @Rule public final TestResources testResources = new TestResources()
+    @Rule public final HttpServer server = new HttpServer()
+
+    @Before
+    public void setup() {
+        distribution.requireOwnUserHomeDir()
+    }
+
+    @Test
+    public void retrievesAndCacheLocalSnapshot() {
+        def producerProject = distribution.testFile('producer.gradle')
+        def consumerProject = distribution.testFile('projectWithMavenSnapshots.gradle')
+
+        // Publish the first snapshot
+        executer.usingBuildScript(producerProject).withTasks('uploadArchives').run()
+
+        // Retrieve the first snapshot
+        executer.usingBuildScript(consumerProject).withTasks('retrieve').run()
+        def jarFile = distribution.testFile('build/testproject-1.0-SNAPSHOT.jar')
+        def snapshot = jarFile.assertIsFile().snapshot()
+
+        // Retrieve again should use cached snapshot
+        executer.usingBuildScript(consumerProject).withTasks('retrieve').run().assertTasksSkipped(':retrieve')
+        jarFile.assertHasNotChangedSince(snapshot)
+
+        // Publish the second snapshot
+        Thread.sleep(1100)
+        executer.usingBuildScript(producerProject).withTasks('uploadArchives').withArguments("-PemptyJar").run()
+
+        // Retrieve again should use updated snapshot
+        executer.usingBuildScript(consumerProject).withTasks('retrieve').run().assertTasksNotSkipped(':retrieve')
+        jarFile.assertHasChangedSince(snapshot)
+    }
+
+    @Test
+    public void retrievesAndCacheSnapshotViaHttp() {
+        server.allowGet('/repo', distribution.testFile('repo'))
+        server.start()
+        String repoUrl = "-PrepoUrl=http://localhost:${server.port}/repo"
+
+        def producerProject = distribution.testFile('producer.gradle')
+        def consumerProject = distribution.testFile('projectWithMavenSnapshots.gradle')
+
+        // Publish the first snapshot
+        executer.usingBuildScript(producerProject).withTasks('uploadArchives').run()
+
+        // Retrieve the first snapshot
+        executer.usingBuildScript(consumerProject).withTasks('retrieve').withArguments(repoUrl).run()
+        def jarFile = distribution.testFile('build/testproject-1.0-SNAPSHOT.jar')
+        def snapshot = jarFile.assertIsFile().snapshot()
+
+        // Publish the second snapshot
+        Thread.sleep(1100)
+        executer.usingBuildScript(producerProject).withTasks('uploadArchives').withArguments("-PemptyJar").run()
+
+        // Retrieve again should use cached snapshot
+        executer.usingBuildScript(consumerProject).withTasks('retrieve').withArguments(repoUrl).run().assertTasksSkipped(':retrieve')
+        jarFile.assertHasNotChangedSince(snapshot)
+
+        // Retrieve again with zero timeout should use updated snapshot
+        executer.usingBuildScript(consumerProject).withTasks('retrieve').withArguments("-PnoTimeout", repoUrl).run().assertTasksNotSkipped(':retrieve')
+        jarFile.assertHasChangedSince(snapshot)
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/maven/SamplesMavenPomGenerationIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/maven/SamplesMavenPomGenerationIntegrationTest.groovy
new file mode 100644
index 0000000..8f049d9
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/maven/SamplesMavenPomGenerationIntegrationTest.groovy
@@ -0,0 +1,152 @@
+/*
+ * 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.maven
+
+import groovy.text.SimpleTemplateEngine
+import org.custommonkey.xmlunit.Diff
+import org.custommonkey.xmlunit.XMLAssert
+import org.custommonkey.xmlunit.examples.RecursiveElementNameAndTextQualifier
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.gradle.util.Resources
+import org.gradle.util.TestFile
+import org.hamcrest.Matchers
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import static org.junit.Assert.*
+import org.gradle.integtests.fixtures.Sample
+
+/**
+ * @author Hans Dockter
+ */
+class SamplesMavenPomGenerationIntegrationTest {
+    @Rule public final GradleDistribution dist = new GradleDistribution()
+    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+
+    private TestFile pomProjectDir
+    private TestFile repoDir
+    private TestFile snapshotRepoDir
+
+    @Rule public Resources resources = new Resources();
+    @Rule public final Sample sample = new Sample('maven/pomGeneration')
+
+    @Before
+    void setUp() {
+        pomProjectDir = sample.dir
+        repoDir = pomProjectDir.file('pomRepo');
+        snapshotRepoDir = pomProjectDir.file('snapshotRepo');
+    }
+    
+    @Test
+    void checkWithNoCustomVersion() {
+        String version = '1.0'
+        String groupId = "gradle"
+        long start = System.currentTimeMillis();
+        executer.inDirectory(pomProjectDir).withTasks('clean', 'uploadArchives', 'install').run()
+        String repoPath = repoPath(groupId, version)
+        compareXmlWithIgnoringOrder(expectedPom(version, groupId),
+                pomFile(repoDir, repoPath, version).text)
+        repoDir.file("$repoPath/mywar-${version}.war").assertIsCopyOf(pomProjectDir.file("target/libs/mywar-${version}.war"))
+        pomProjectDir.file('build').assertDoesNotExist()
+        pomProjectDir.file('target').assertIsDir()
+        checkInstall(start, pomProjectDir, version, groupId)
+    }
+
+    @Test
+    void checkWithCustomVersion() {
+        long start = System.currentTimeMillis();
+        String version = "1.0MVN"
+        String groupId = "deployGroup"
+        executer.inDirectory(pomProjectDir).withArguments("-PcustomVersion=${version}").withTasks('clean', 'uploadArchives', 'install').run()
+        String repoPath = repoPath(groupId, version)
+        compareXmlWithIgnoringOrder(expectedPom(version, groupId),
+                pomFile(repoDir, repoPath, version).text)
+        repoDir.file("$repoPath/mywar-${version}.war").assertIsCopyOf(pomProjectDir.file("target/libs/mywar-1.0.war"))
+        repoDir.file("$repoPath/mywar-${version}-javadoc.zip").assertIsCopyOf(pomProjectDir.file("target/distributions/mywar-1.0-javadoc.zip"))
+        pomProjectDir.file('build').assertDoesNotExist()
+        pomProjectDir.file('target').assertIsDir()
+        checkInstall(start, pomProjectDir, version, 'installGroup')
+    }
+
+    @Test
+    void checkWithSnapshotVersion() {
+        String version = '1.0-SNAPSHOT'
+        String groupId = "deployGroup"
+        long start = System.currentTimeMillis();
+        executer.inDirectory(pomProjectDir).withArguments("-PcustomVersion=${version}").withTasks('clean', 'uploadArchives', 'install').run()
+        String repoPath = repoPath(groupId, version)
+        File pomFile = pomFile(snapshotRepoDir, repoPath, version)
+        compareXmlWithIgnoringOrder(expectedPom(version, groupId), pomFile.text)
+        new TestFile(new File(pomFile.absolutePath.replace(".pom", ".war"))).assertIsFile()
+        pomProjectDir.file('build').assertDoesNotExist()
+        pomProjectDir.file('target').assertIsDir()
+        checkInstall(start, pomProjectDir, version, 'installGroup')
+    }
+
+    @Test
+    void writeNewPom() {
+        executer.inDirectory(pomProjectDir).withTasks('clean', 'writeNewPom').run()
+        compareXmlWithIgnoringOrder(expectedPom(null, null, 'pomGeneration/expectedNewPom.txt'),
+                pomProjectDir.file("target/newpom.xml").text)
+    }
+
+    @Test
+    void writeDeployerPom() {
+        String version = '1.0'
+        String groupId = "gradle"
+        executer.inDirectory(pomProjectDir).withTasks('clean', 'writeDeployerPom').run()
+        compareXmlWithIgnoringOrder(expectedPom(version, groupId), pomProjectDir.file("target/deployerpom.xml").text)
+    }
+    
+    static String repoPath(String group, String version) {
+        "$group/mywar/$version"
+    }
+
+    static File pomFile(TestFile repoDir, String repoPath, String version) {
+        TestFile versionDir = repoDir.file(repoPath)
+        List matches = versionDir.listFiles().findAll { it.name.endsWith('.pom') }
+        assert matches.size() == 1
+        matches[0]
+    }
+
+    void checkInstall(long start, TestFile pomProjectDir, String version, String groupId) {
+        TestFile localMavenRepo = new TestFile(pomProjectDir.file("target/localRepoPath.txt").text as File)
+        TestFile installedFile = localMavenRepo.file("$groupId/mywar/$version/mywar-${version}.war")
+        TestFile installedJavadocFile = localMavenRepo.file("$groupId/mywar/$version/mywar-${version}-javadoc.zip")
+        TestFile installedPom = localMavenRepo.file("$groupId/mywar/$version/mywar-${version}.pom")
+        installedFile.assertIsCopyOf(pomProjectDir.file("target/libs/mywar-1.0.war"))
+        installedJavadocFile.assertIsCopyOf(pomProjectDir.file("target/distributions/mywar-1.0-javadoc.zip"))
+        installedPom.assertIsFile()
+        assert start.intdiv(2000) <= installedFile.lastModified().intdiv(2000)
+        assert start.intdiv(2000) <= installedJavadocFile.lastModified().intdiv(2000)
+        compareXmlWithIgnoringOrder(expectedPom(version, groupId), installedPom.text)
+    }
+    
+    private String expectedPom(String version, String groupId, String path = 'pomGeneration/expectedPom.txt') {
+        SimpleTemplateEngine templateEngine = new SimpleTemplateEngine();
+        String text = resources.getResource(path).text
+        return templateEngine.createTemplate(text).make(version: version, groupId: groupId)
+    }
+
+    private static void compareXmlWithIgnoringOrder(String expectedXml, String actualXml) {
+        Diff diff = new Diff(expectedXml, actualXml)
+        diff.overrideElementQualifier(new RecursiveElementNameAndTextQualifier())
+        XMLAssert.assertXMLEqual(diff, true);
+        Assert.assertThat(actualXml, Matchers.startsWith(String.format('<?xml version="1.0" encoding="UTF-8"?>')))
+    }
+}
\ No newline at end of file
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/maven/SamplesMavenQuickstartIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/maven/SamplesMavenQuickstartIntegrationTest.groovy
new file mode 100644
index 0000000..254c4ba
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/maven/SamplesMavenQuickstartIntegrationTest.groovy
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.maven
+
+import groovy.text.SimpleTemplateEngine
+import org.custommonkey.xmlunit.Diff
+import org.custommonkey.xmlunit.XMLAssert
+import org.custommonkey.xmlunit.examples.RecursiveElementNameAndTextQualifier
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.util.Resources
+import org.gradle.util.TestFile
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import static org.junit.Assert.assertEquals
+
+/**
+ * @author Hans Dockter
+ */
+class SamplesMavenQuickstartIntegrationTest {
+    @Rule public final GradleDistribution dist = new GradleDistribution()
+    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+    @Rule public Resources resources = new Resources();
+    @Rule public final Sample sample = new Sample('maven/quickstart')
+
+    private TestFile pomProjectDir
+    private TestFile repoDir
+
+    @Before
+    void setUp() {
+        pomProjectDir = sample.dir
+        repoDir = pomProjectDir.file('pomRepo');
+    }
+
+    @Test
+    void checkDeployAndInstall() {
+        String version = '1.0'
+        String groupId = "gradle"
+        long start = System.currentTimeMillis();
+        executer.inDirectory(pomProjectDir).withTasks('clean', 'uploadArchives', 'install').run()
+        String repoPath = repoPath(groupId, version)
+        compareXmlWithIgnoringOrder(expectedPom(version, groupId),
+                pomFile(repoDir, repoPath, version).text)
+        repoDir.file("$repoPath/quickstart-1.0.jar").assertIsCopyOf(pomProjectDir.file('build/libs/quickstart-1.0.jar'))
+        checkInstall(start, pomProjectDir, version, groupId)
+    }
+
+    static String repoPath(String group, String version) {
+        "$group/quickstart/$version"
+    }
+
+    static File pomFile(TestFile repoDir, String repoPath, String version) {
+        TestFile versionDir = repoDir.file(repoPath)
+        List matches = versionDir.listFiles().findAll { it.name.endsWith('.pom') }
+        assertEquals(1, matches.size())
+        matches[0]
+    }
+
+    void checkInstall(long start, TestFile pomProjectDir, String version, String groupId) {
+        TestFile localMavenRepo = new TestFile(pomProjectDir.file("build/localRepoPath.txt").text as File)
+        TestFile installedFile = localMavenRepo.file("$groupId/quickstart/$version/quickstart-${version}.jar")
+        TestFile installedPom = localMavenRepo.file("$groupId/quickstart/$version/quickstart-${version}.pom")
+        installedFile.assertIsCopyOf(pomProjectDir.file('build/libs/quickstart-1.0.jar'))
+        installedPom.assertIsFile()
+        assert start.intdiv(2000) <= installedFile.lastModified().intdiv(2000)
+        compareXmlWithIgnoringOrder(expectedPom(version, groupId), installedPom.text)
+    }
+
+    private String expectedPom(String version, String groupId) {
+        SimpleTemplateEngine templateEngine = new SimpleTemplateEngine();
+        String text = resources.getResource('pomGeneration/expectedQuickstartPom.txt').text
+        return templateEngine.createTemplate(text).make(version: version, groupId: groupId)
+    }
+
+    private static void compareXmlWithIgnoringOrder(String expectedXml, String actualXml) {
+        Diff diff = new Diff(expectedXml, actualXml)
+        diff.overrideElementQualifier(new RecursiveElementNameAndTextQualifier())
+        XMLAssert.assertXMLEqual(diff, true);
+    }
+}
\ No newline at end of file
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/CoreAutoTestedSamplesTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/CoreAutoTestedSamplesTest.groovy
new file mode 100644
index 0000000..0cbc2c0
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/CoreAutoTestedSamplesTest.groovy
@@ -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.integtests.samples
+
+import org.gradle.integtests.fixtures.internal.AbstractAutoTestedSamplesTest
+import org.junit.Test
+
+/**
+ * Author: Szczepan Faber, created at: 3/28/11
+ */
+class CoreAutoTestedSamplesTest extends AbstractAutoTestedSamplesTest {
+
+    @Test
+    void runSamples() {
+        runSamplesFrom("subprojects/core/src/main/groovy/org/gradle/api/tasks")
+    }
+}
\ No newline at end of file
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/PluginsAutoTestedSamplesTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/PluginsAutoTestedSamplesTest.groovy
new file mode 100644
index 0000000..630dd2c
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/PluginsAutoTestedSamplesTest.groovy
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.samples
+
+import org.gradle.integtests.fixtures.internal.AbstractAutoTestedSamplesTest
+import org.junit.Test
+
+/**
+ * Author: Szczepan Faber, created at: 3/28/11
+ */
+class PluginsAutoTestedSamplesTest extends AbstractAutoTestedSamplesTest {
+
+    @Test
+    void runSamples() {
+        runSamplesFrom("subprojects/plugins/src/main")
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/testng/SampleTestNGIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/testng/SampleTestNGIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/testng/SampleTestNGIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/testng/SampleTestNGIntegrationTest.groovy
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/testng/TestNGExecutionResult.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/testng/TestNGExecutionResult.groovy
new file mode 100644
index 0000000..6942fb9
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/testng/TestNGExecutionResult.groovy
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 assertTestsSkipped(String... testNames) {
+        throw new UnsupportedOperationException()
+    }
+
+    TestClassExecutionResult assertTestSkipped(String name) {
+        def testMethodNode = findTestMethod(name)
+        assertEquals('SKIP', testMethodNode. at status as String)
+        this
+    }
+
+    TestClassExecutionResult assertTestFailed(String name, Matcher<? super String>... messageMatchers) {
+        def testMethodNode = findTestMethod(name)
+        assertEquals('FAIL', testMethodNode. at status as String)
+
+        def exceptions = testMethodNode.exception
+        assertThat(exceptions.size(), equalTo(messageMatchers.length))
+
+        for (int i = 0; i < messageMatchers.length; i++) {
+            assertThat(exceptions[i].message[0].text().trim(), messageMatchers[i])
+        }
+        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/core/src/integTest/groovy/org/gradle/integtests/testng/TestNGIntegrationProject.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/testng/TestNGIntegrationProject.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/testng/TestNGIntegrationProject.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/testng/TestNGIntegrationProject.groovy
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/integtests/testng/TestNGIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/testng/TestNGIntegrationTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/groovy/org/gradle/integtests/testng/TestNGIntegrationTest.groovy
rename to subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/testng/TestNGIntegrationTest.groovy
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/tooling/SamplesToolingApiIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/tooling/SamplesToolingApiIntegrationTest.groovy
new file mode 100644
index 0000000..7e2af46
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/tooling/SamplesToolingApiIntegrationTest.groovy
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.tooling
+
+import org.gradle.integtests.fixtures.ExecutionResult
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.integtests.fixtures.internal.IntegrationTestHint
+import org.junit.Rule
+import spock.lang.Specification
+import org.gradle.integtests.fixtures.UsesSample
+
+class SamplesToolingApiIntegrationTest extends Specification {
+    @Rule public final GradleDistribution distribution = new GradleDistribution()
+    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+    @Rule public final Sample sample = new Sample()
+
+    @UsesSample('toolingApi/model')
+    def canUseToolingApiToDetermineProjectClasspath() {
+        def projectDir = sample.dir
+        Properties props = new Properties()
+        props['toolingApiRepo'] = distribution.libsRepo.toURI().toString()
+        props['gradleDistribution'] = distribution.gradleHomeDir.toString()
+        projectDir.file('gradle.properties').withOutputStream {outstr ->
+            props.store(outstr, 'props')
+        }
+        projectDir.file('settings.gradle').text = '// to stop search upwards'
+
+        when:
+        def result = run(projectDir, 'run')
+
+        then:
+        result.output.contains("gradle-tooling-api-${distribution.version}.jar")
+        result.output.contains("gradle-core-${distribution.version}.jar")
+        result.output.contains("gradle-wrapper-${distribution.version}.jar")
+    }
+
+    @UsesSample('toolingApi/build')
+    def canUseToolingApiToRunABuild() {
+        def projectDir = sample.dir
+        Properties props = new Properties()
+        props['toolingApiRepo'] = distribution.libsRepo.toURI().toString()
+        props['gradleDistribution'] = distribution.gradleHomeDir.toString()
+        projectDir.file('gradle.properties').withOutputStream {outstr ->
+            props.store(outstr, 'props')
+        }
+        projectDir.file('settings.gradle').text = '// to stop search upwards'
+
+        when:
+        def result = run(projectDir, 'run')
+
+        then:
+        result.output.contains("Welcome to Gradle ${distribution.version}.")
+    }
+
+    private ExecutionResult run(dir, task) {
+        try {
+            return executer.inDirectory(dir).withTasks(task).run()
+        } catch (Exception e) {
+            throw new IntegrationTestHint(e);
+        }
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApi.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApi.groovy
new file mode 100644
index 0000000..c1dab27
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApi.groovy
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.tooling
+
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.RuleHelper
+import org.gradle.integtests.fixtures.internal.IntegrationTestHint
+import org.gradle.tooling.GradleConnector
+import org.gradle.tooling.ProjectConnection
+import org.gradle.tooling.UnsupportedVersionException
+import org.junit.rules.MethodRule
+import org.junit.runners.model.FrameworkMethod
+import org.junit.runners.model.Statement
+import java.util.concurrent.TimeUnit
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.slf4j.LoggerFactory
+import org.slf4j.Logger
+import org.gradle.integtests.fixtures.DaemonGradleExecuter
+
+class ToolingApi implements MethodRule {
+    private static final Logger LOGGER = LoggerFactory.getLogger(ToolingApi)
+    File projectDir
+    private GradleDistribution dist
+    private final List<Closure> connectorConfigurers = []
+
+    Statement apply(Statement base, FrameworkMethod method, Object target) {
+        dist = RuleHelper.getField(target, GradleDistribution.class)
+        return base
+    }
+
+    def withConnector(Closure cl) {
+        connectorConfigurers << cl
+    }
+
+    def withConnection(Closure cl) {
+        GradleConnector connector = connector()
+        try {
+            withConnectionRaw(connector, cl)
+        } catch (UnsupportedVersionException e) {
+            throw new IntegrationTestHint(e);
+        }
+    }
+
+    def maybeFailWithConnection(Closure cl) {
+        GradleConnector connector = connector()
+        try {
+            withConnectionRaw(connector, cl)
+        } catch (Throwable e) {
+            return e
+        }
+        return null
+    }
+
+    private def withConnectionRaw(GradleConnector connector, Closure cl) {
+        ProjectConnection connection = connector.connect()
+        try {
+            return cl.call(connection)
+        } finally {
+            connection.close()
+        }
+    }
+
+    def File getProjectDir() {
+        return projectDir ?: dist.testDir
+    }
+
+    private def connector() {
+        GradleConnector connector = GradleConnector.newConnector()
+        connector.useGradleUserHomeDir(new File(dist.userHomeDir.absolutePath))
+        connector.forProjectDirectory(new File(getProjectDir().absolutePath))
+        connector.searchUpwards(false)
+        if (GradleDistributionExecuter.executer == GradleDistributionExecuter.Executer.embedded) {
+            LOGGER.info("Using embedded tooling API provider");
+            connector.useClasspathDistribution()
+            connector.embedded(true)
+        } else {
+            LOGGER.info("Using daemon tooling API provider");
+            connector.useInstallation(new File(dist.gradleHomeDir.absolutePath))
+            connector.embedded(false)
+            connector.daemonMaxIdleTime(5, TimeUnit.MINUTES)
+            DaemonGradleExecuter.registerDaemon(dist.userHomeDir)
+        }
+        connectorConfigurers.each {
+            it.call(connector)
+        }
+        return connector
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiBuildExecutionIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiBuildExecutionIntegrationTest.groovy
new file mode 100644
index 0000000..0c0169e
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiBuildExecutionIntegrationTest.groovy
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.tooling
+
+import org.gradle.tooling.model.BuildableProject
+import org.gradle.tooling.model.eclipse.EclipseProject
+import org.gradle.tooling.model.Task
+import org.gradle.tooling.BuildException
+import org.gradle.tooling.ProgressListener
+
+class ToolingApiBuildExecutionIntegrationTest extends ToolingApiSpecification {
+    def "can build the set of tasks for a project"() {
+        dist.testFile('build.gradle') << '''
+task a {
+   description = 'this is task a'
+}
+task b
+task c
+'''
+
+        when:
+        def project = withConnection { connection -> connection.getModel(BuildableProject.class) }
+
+        then:
+        def taskA = project.tasks.find { it.name == 'a' }
+        taskA != null
+        taskA.path == ':a'
+        taskA.description == 'this is task a'
+        taskA.project == project
+        project.tasks.find { it.name == 'b' }
+        project.tasks.find { it.name == 'c' }
+    }
+
+    def "can execute a build for a project"() {
+        dist.testFile('settings.gradle') << 'rootProject.name="test"'
+        dist.testFile('build.gradle') << '''
+apply plugin: 'java'
+'''
+        when:
+        withConnection { connection ->
+            def build = connection.newBuild()
+            build.forTasks('jar')
+            build.run()
+        }
+
+        then:
+        dist.testFile('build/libs/test.jar').assertIsFile()
+
+        when:
+        withConnection { connection ->
+            BuildableProject project = connection.getModel(BuildableProject.class)
+            Task clean = project.tasks.find { it.name == 'clean' }
+            def build = connection.newBuild()
+            build.forTasks(clean)
+            build.run()
+        }
+
+        then:
+        dist.testFile('build/libs/test.jar').assertDoesNotExist()
+    }
+
+    def "receives progress and logging while the build is executing"() {
+        dist.testFile('build.gradle') << '''
+System.out.println 'this is stdout'
+System.err.println 'this is stderr'
+'''
+        def stdout = new ByteArrayOutputStream()
+        def stderr = new ByteArrayOutputStream()
+        def progressMessages = []
+
+        when:
+        withConnection { connection ->
+            def build = connection.newBuild()
+            build.standardOutput = stdout
+            build.standardError = stderr
+            build.addProgressListener({ event -> progressMessages << event.description } as ProgressListener)
+            build.run()
+        }
+
+        then:
+        stdout.toString().contains('this is stdout')
+        stderr.toString().contains('this is stderr')
+        progressMessages.size() >= 2
+        progressMessages.pop() == ''
+        progressMessages.every { it }
+    }
+
+    def "tooling api reports build failure"() {
+        dist.testFile('build.gradle') << 'broken'
+
+        when:
+        withConnection { connection ->
+            return connection.newBuild().forTasks('jar').run()
+        }
+
+        then:
+        BuildException e = thrown()
+        e.message.startsWith('Could not execute build using Gradle')
+        e.cause.message.contains('A problem occurred evaluating root project')
+    }
+
+    def "can build the set of tasks for an Eclipse project"() {
+        dist.testFile('build.gradle') << '''
+task a {
+   description = 'this is task a'
+}
+task b
+task c
+'''
+
+        when:
+        def project = withConnection { connection -> connection.getModel(EclipseProject.class) }
+
+        then:
+        def taskA = project.tasks.find { it.name == 'a' }
+        taskA != null
+        taskA.path == ':a'
+        taskA.description == 'this is task a'
+        taskA.project == project
+        project.tasks.find { it.name == 'b' }
+        project.tasks.find { it.name == 'c' }
+    }
+
+    def "does not resolve dependencies when building the set of tasks for a project"() {
+        dist.testFile('build.gradle') << '''
+apply plugin: 'java'
+dependencies {
+    compile files { throw new RuntimeException('broken') }
+}
+'''
+
+        when:
+        def project = withConnection { connection -> connection.getModel(BuildableProject.class) }
+
+        then:
+        !project.tasks.empty
+    }
+
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiEclipseModelIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiEclipseModelIntegrationTest.groovy
new file mode 100644
index 0000000..b3e0a16
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiEclipseModelIntegrationTest.groovy
@@ -0,0 +1,336 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.tooling
+
+import org.gradle.tooling.model.ExternalDependency
+import org.gradle.tooling.model.eclipse.EclipseProject
+import org.gradle.tooling.model.eclipse.HierarchicalEclipseProject
+
+class ToolingApiEclipseModelIntegrationTest extends ToolingApiSpecification {
+
+    def "can build the eclipse model for a java project"() {
+        def projectDir = dist.testDir
+        projectDir.file('build.gradle').text = '''
+apply plugin: 'java'
+description = 'this is a project'
+'''
+        projectDir.file('settings.gradle').text = 'rootProject.name = \"test project\"'
+
+        when:
+        HierarchicalEclipseProject minimalProject = withConnection { connection -> connection.getModel(HierarchicalEclipseProject.class) }
+
+        then:
+        minimalProject.path == ':'
+        minimalProject.name == 'test project'
+        minimalProject.description == 'this is a project'
+        minimalProject.projectDirectory == projectDir
+        minimalProject.parent == null
+        minimalProject.children.empty
+
+        when:
+        EclipseProject fullProject = withConnection { connection -> connection.getModel(EclipseProject.class) }
+
+        then:
+        fullProject.path == ':'
+        fullProject.name == 'test project'
+        fullProject.description == 'this is a project'
+        fullProject.projectDirectory == projectDir
+        fullProject.parent == null
+        fullProject.children.empty
+    }
+
+    def "can build the eclipse model for an empty project"() {
+        when:
+        HierarchicalEclipseProject minimalProject = withConnection { connection -> connection.getModel(HierarchicalEclipseProject.class) }
+
+        then:
+        minimalProject != null
+
+        minimalProject.description == null
+        minimalProject.parent == null
+        minimalProject.children.empty
+        minimalProject.sourceDirectories.empty
+        minimalProject.projectDependencies.empty
+
+        when:
+        EclipseProject fullProject = withConnection { connection -> connection.getModel(EclipseProject.class) }
+
+        then:
+        fullProject != null
+
+        fullProject.description == null
+        fullProject.parent == null
+        fullProject.children.empty
+        fullProject.sourceDirectories.empty
+        fullProject.classpath.empty
+        fullProject.projectDependencies.empty
+    }
+
+    def "can build the eclipse source directories for a java project"() {
+        def projectDir = dist.testDir
+        projectDir.file('build.gradle').text = "apply plugin: 'java'"
+
+        projectDir.create {
+            src {
+                main {
+                    java {}
+                    resources {}
+                }
+                test {
+                    java {}
+                    resources {}
+                }
+            }
+        }
+
+        when:
+        HierarchicalEclipseProject minimalProject = withConnection { connection -> connection.getModel(HierarchicalEclipseProject.class) }
+
+        then:
+        minimalProject != null
+
+        minimalProject.sourceDirectories.size() == 4
+        minimalProject.sourceDirectories[0].path == 'src/main/java'
+        minimalProject.sourceDirectories[0].directory == projectDir.file('src/main/java')
+        minimalProject.sourceDirectories[1].path == 'src/main/resources'
+        minimalProject.sourceDirectories[1].directory == projectDir.file('src/main/resources')
+        minimalProject.sourceDirectories[2].path == 'src/test/java'
+        minimalProject.sourceDirectories[2].directory == projectDir.file('src/test/java')
+        minimalProject.sourceDirectories[3].path == 'src/test/resources'
+        minimalProject.sourceDirectories[3].directory == projectDir.file('src/test/resources')
+
+        when:
+        EclipseProject fullProject = withConnection { connection -> connection.getModel(EclipseProject.class) }
+
+        then:
+        fullProject != null
+
+        fullProject.sourceDirectories.size() == 4
+        fullProject.sourceDirectories[0].path == 'src/main/java'
+        fullProject.sourceDirectories[0].directory == projectDir.file('src/main/java')
+        fullProject.sourceDirectories[1].path == 'src/main/resources'
+        fullProject.sourceDirectories[1].directory == projectDir.file('src/main/resources')
+        fullProject.sourceDirectories[2].path == 'src/test/java'
+        fullProject.sourceDirectories[2].directory == projectDir.file('src/test/java')
+        fullProject.sourceDirectories[3].path == 'src/test/resources'
+        fullProject.sourceDirectories[3].directory == projectDir.file('src/test/resources')
+    }
+
+    def "can build the eclipse external dependencies for a java project"() {
+        def projectDir = dist.testDir
+        projectDir.file('settings.gradle').text = '''
+include "a"
+rootProject.name = 'root'
+'''
+        projectDir.file('build.gradle').text = '''
+allprojects { apply plugin: 'java' }
+repositories { mavenCentral() }
+dependencies {
+    compile 'commons-lang:commons-lang:2.5'
+    compile project(':a')
+    runtime 'commons-io:commons-io:1.4'
+}
+'''
+
+        when:
+        EclipseProject eclipseProject = withConnection { connection -> connection.getModel(EclipseProject.class) }
+
+        then:
+        eclipseProject != null
+
+        eclipseProject.classpath.size() == 2
+        eclipseProject.classpath.every { it instanceof ExternalDependency }
+        eclipseProject.classpath.collect { it.file.name } as Set == ['commons-lang-2.5.jar', 'commons-io-1.4.jar' ] as Set
+        eclipseProject.classpath.collect { it.source?.name } as Set == ['commons-lang-2.5-sources.jar', 'commons-io-1.4-sources.jar'] as Set
+        eclipseProject.classpath.collect { it.javadoc?.name } as Set == [null, null] as Set
+    }
+
+    def "minimal Eclipse model does not attempt to resolve external dependencies"() {
+        def projectDir = dist.testDir
+        projectDir.file('settings.gradle').text = 'include "child"'
+        projectDir.file('build.gradle').text = '''
+apply plugin: 'java'
+dependencies {
+    compile project(':child')
+    compile files { throw new RuntimeException() }
+}
+project(':child') {
+    apply plugin: 'java'
+    dependencies { compile files { throw new RuntimeException() } }
+}
+'''
+
+        when:
+        HierarchicalEclipseProject project = withConnection { connection -> connection.getModel(HierarchicalEclipseProject.class) }
+
+        then:
+        project.projectDependencies.size() == 1
+        project.projectDependencies[0].path == 'child'
+    }
+
+    //TODO SF: write a test that checks if minimal project has necessary project dependencies
+
+    def "can build the minimal Eclipse model for a java project with the idea plugin applied"() {
+        def projectDir = dist.testDir
+        projectDir.file('build.gradle').text = '''
+apply plugin: 'java'
+apply plugin: 'idea'
+
+dependencies {
+    compile files { throw new RuntimeException('should not be resolving this') }
+}
+'''
+
+        when:
+        HierarchicalEclipseProject minimalProject = withConnection { connection -> connection.getModel(HierarchicalEclipseProject.class) }
+
+        then:
+        minimalProject != null
+    }
+
+    def "can build the eclipse project dependencies for a java project"() {
+        def projectDir = dist.testDir
+        projectDir.file('settings.gradle').text = '''
+include "a", "a:b"
+rootProject.name = 'root'
+'''
+        projectDir.file('build.gradle').text = '''
+allprojects {
+    apply plugin: 'java'
+}
+project(':a') {
+    dependencies {
+        compile project(':')
+        compile project(':a:b')
+    }
+}
+'''
+
+        when:
+        HierarchicalEclipseProject minimalModel = withConnection { connection -> connection.getModel(HierarchicalEclipseProject.class) }
+
+        then:
+        HierarchicalEclipseProject minimalProject = minimalModel.children[0]
+
+        minimalProject.projectDependencies.size() == 2
+
+        minimalProject.projectDependencies[0].path == 'root'
+        minimalProject.projectDependencies[0].targetProject == minimalModel
+
+        minimalProject.projectDependencies[1].path == 'b'
+        minimalProject.projectDependencies[1].targetProject == minimalProject.children[0]
+
+        when:
+        EclipseProject fullModel = withConnection { connection -> connection.getModel(EclipseProject.class) }
+
+        then:
+        EclipseProject fullProject = fullModel.children[0]
+
+        fullProject.projectDependencies.size() == 2
+
+        fullProject.projectDependencies[0].path == 'root'
+        fullProject.projectDependencies[0].targetProject == fullModel
+
+        fullProject.projectDependencies[1].path == 'b'
+        fullProject.projectDependencies[1].targetProject == fullProject.children[0]
+    }
+
+    def "can build project dependencies with targetProject references for complex scenarios"() {
+        def projectDir = dist.testDir
+        projectDir.file('settings.gradle').text = '''
+include "c", "a", "a:b"
+rootProject.name = 'root'
+'''
+        projectDir.file('build.gradle').text = '''
+allprojects {
+    apply plugin: 'java'
+}
+project(':a') {
+    dependencies {
+        compile project(':')
+        compile project(':a:b')
+        compile project(':c')
+    }
+}
+project(':c') {
+    dependencies {
+        compile project(':a:b')
+    }
+}
+'''
+
+        when:
+        EclipseProject rootProject = withConnection { connection -> connection.getModel(EclipseProject.class) }
+
+        then:
+        def projectC = rootProject.children.find { it.name == 'c'}
+        def projectA = rootProject.children.find { it.name == 'a'}
+        def projectAB = projectA.children.find { it.name == 'b' }
+
+        projectC.projectDependencies.any {it.targetProject == projectAB}
+
+        projectA.projectDependencies.any {it.targetProject == projectAB}
+        projectA.projectDependencies.any {it.targetProject == projectC}
+        projectA.projectDependencies.any {it.targetProject == rootProject}
+    }
+
+    def "can build the eclipse project hierarchy for a multi-project build"() {
+        def projectDir = dist.testDir
+        projectDir.file('settings.gradle').text = '''
+            include "child1", "child2", "child1:grandChild1"
+            rootProject.name = 'root'
+'''
+        projectDir.file('child1').mkdirs()
+
+        when:
+        EclipseProject rootProject = withConnection { connection -> connection.getModel(EclipseProject.class) }
+
+        then:
+        rootProject != null
+        rootProject.name == 'root'
+        rootProject.parent == null
+
+        rootProject.children.size() == 2
+
+        EclipseProject child1 = rootProject.children[0]
+        child1.name == 'child1'
+        child1.parent == rootProject
+        child1.children.size() == 1
+
+        EclipseProject child1Child1 = child1.children[0]
+        child1Child1.name == 'grandChild1'
+        child1Child1.parent == child1
+        child1Child1.children.size() == 0
+
+        EclipseProject child2 = rootProject.children[1]
+        child2.name == 'child2'
+        child2.parent == rootProject
+        child2.children.size() == 0
+
+        when:
+        toolingApi.withConnector { connector ->
+            connector.searchUpwards(true)
+            connector.forProjectDirectory(new File(projectDir, 'child1'))
+        }
+        EclipseProject child = toolingApi.withConnection { connection -> connection.getModel(EclipseProject.class) }
+
+        then:
+        child.name == 'child1'
+        child.parent != null
+        child.parent.name == 'root'
+        child.children.size() == 1
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiHonorsProjectCustomizationsIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiHonorsProjectCustomizationsIntegrationTest.groovy
new file mode 100644
index 0000000..91da9d8
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiHonorsProjectCustomizationsIntegrationTest.groovy
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.tooling
+
+import org.gradle.tooling.model.eclipse.EclipseProject
+
+class ToolingApiHonorsProjectCustomizationsIntegrationTest extends ToolingApiSpecification {
+
+    def "should honour reconfigured project names"() {
+        def projectDir = dist.testDir
+        projectDir.file('build.gradle').text = '''
+allprojects {
+    apply plugin: 'java'
+    apply plugin: 'eclipse'
+}
+
+project(':api') {
+    eclipseProject { projectName = 'gradle-api' }
+}
+
+project(':impl') {
+    eclipseProject { projectName = 'gradle-impl' }
+}
+'''
+        projectDir.file('settings.gradle').text = "include 'api', 'impl'"
+
+        when:
+        EclipseProject eclipseProject = withConnection { connection -> connection.getModel(EclipseProject.class) }
+
+        then:
+        EclipseProject api = eclipseProject.children[1]
+        assert api.name == 'gradle-api'
+        EclipseProject impl = eclipseProject.children[0]
+        assert impl.name == 'gradle-impl'
+    }
+
+    def "should deduplicate project names"() {
+        def projectDir = dist.testDir
+        projectDir.file('build.gradle').text = '''
+allprojects {
+    apply plugin: 'java'
+}
+'''
+        projectDir.file('settings.gradle').text = "include 'services:api', 'contrib:api'"
+
+        when:
+        EclipseProject eclipseProject = withConnection { connection -> connection.getModel(EclipseProject.class) }
+
+        then:
+        String grandChildOne = eclipseProject.children[0].children[0].name
+        String grandChildTwo = eclipseProject.children[1].children[0].name
+        assert grandChildOne != grandChildTwo : "Deduplication logic should make that project names are not the same."
+    }
+
+    def "can have overlapping source and resource directories"() {
+        def projectDir = dist.testDir
+        projectDir.file('build.gradle').text = '''
+apply plugin: 'java'
+apply plugin: 'eclipse'
+
+sourceSets {
+    main {
+        java { srcDir 'src' }
+        resources { srcDir 'src' }
+    }
+
+    test {
+        java { srcDir 'test' }
+        resources { srcDir 'testResources' }
+    }
+}
+'''
+        //if we don't create the folders eclipse plugin will not build the classpath
+        projectDir.create {
+            src {}
+            test {}
+            testResources {}
+        }
+
+        when:
+        EclipseProject eclipseProject = withConnection { connection -> connection.getModel(EclipseProject.class) }
+
+        then:
+        eclipseProject.sourceDirectories.size() == 3
+        eclipseProject.sourceDirectories[0].path == 'src'
+        eclipseProject.sourceDirectories[1].path == 'testResources'
+        eclipseProject.sourceDirectories[2].path == 'test'
+    }
+
+    def "can enable download of Javadoc for external dependencies"() {
+        def projectDir = dist.testDir
+        projectDir.file('build.gradle').text = '''
+apply plugin: 'java'
+apply plugin: 'eclipse'
+repositories { mavenCentral() }
+dependencies {
+    compile 'commons-lang:commons-lang:2.5'
+    runtime 'commons-io:commons-io:1.4'
+}
+eclipse { classpath { downloadJavadoc = true } }
+'''
+
+        when:
+        EclipseProject eclipseProject = withConnection { connection -> connection.getModel(EclipseProject.class) }
+
+        then:
+        eclipseProject.classpath.size() == 2
+        eclipseProject.classpath.collect { it.file.name } as Set == ['commons-lang-2.5.jar', 'commons-io-1.4.jar' ] as Set
+        eclipseProject.classpath.collect { it.source?.name } as Set == ['commons-lang-2.5-sources.jar', 'commons-io-1.4-sources.jar'] as Set
+        eclipseProject.classpath.collect { it.javadoc?.name } as Set == ['commons-lang-2.5-javadoc.jar', 'commons-io-1.4-javadoc.jar'] as Set
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiIntegrationTest.groovy
new file mode 100644
index 0000000..235b34f
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiIntegrationTest.groovy
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.tooling
+
+import org.gradle.integtests.fixtures.BasicGradleDistribution
+import org.gradle.tooling.UnsupportedVersionException
+import org.gradle.tooling.model.Project
+import org.gradle.util.GradleVersion
+
+class ToolingApiIntegrationTest extends ToolingApiSpecification {
+    final BasicGradleDistribution otherVersion = dist.previousVersion('1.0-milestone-3-20110424172210+1000')
+
+    def "tooling api uses to the current version of gradle when none has been specified"() {
+        def projectDir = dist.testDir
+        projectDir.file('build.gradle').text = "assert gradle.gradleVersion == '${GradleVersion.current().version}'"
+
+        when:
+        Project model = toolingApi.withConnection { connection -> connection.getModel(Project.class) }
+
+        then:
+        model != null
+    }
+
+    def "tooling api uses the wrapper properties to determine which version to use"() {
+        def projectDir = dist.testDir
+        projectDir.file('build.gradle').text = """
+task wrapper(type: Wrapper) { distributionUrl = '${otherVersion.binDistribution.toURI()}' }
+task check << { assert gradle.gradleVersion == '${GradleVersion.current().version}' }
+"""
+        dist.executer().withTasks('wrapper').run()
+
+        when:
+        toolingApi.withConnection { connection -> connection.newBuild().forTasks('check').run() }
+
+        then:
+        notThrown(Throwable)
+    }
+
+    def "can specify a gradle installation to use"() {
+        def projectDir = dist.testDir
+        projectDir.file('build.gradle').text = "assert gradle.gradleVersion == '${otherVersion.version}'"
+
+        when:
+        toolingApi.withConnector { connector -> connector.useInstallation(otherVersion.gradleHomeDir) }
+        Project model = toolingApi.withConnection { connection -> connection.getModel(Project.class) }
+
+        then:
+        model != null
+    }
+
+    def "can specify a gradle distribution to use"() {
+        def projectDir = dist.testDir
+        projectDir.file('build.gradle').text = "assert gradle.gradleVersion == '${otherVersion.version}'"
+
+        when:
+        toolingApi.withConnector { connector -> connector.useDistribution(otherVersion.binDistribution.toURI()) }
+        Project model = toolingApi.withConnection { connection -> connection.getModel(Project.class) }
+
+        then:
+        model != null
+    }
+
+    def "can specify a gradle version to use"() {
+        def projectDir = dist.testDir
+        projectDir.file('build.gradle').text = "assert gradle.gradleVersion == '${otherVersion.version}'"
+
+        when:
+        toolingApi.withConnector { connector -> connector.useGradleVersion(otherVersion.version) }
+        Project model = toolingApi.withConnection { connection -> connection.getModel(Project.class) }
+
+        then:
+        model != null
+    }
+
+    def "tooling api reports an error when the specified gradle version does not support the tooling api"() {
+        def dist = dist.previousVersion('0.9.2').binDistribution
+
+        when:
+        toolingApi.withConnector { connector -> connector.useDistribution(dist.toURI()) }
+        def e = toolingApi.maybeFailWithConnection { connection -> connection.getModel(Project.class) }
+
+        then:
+        e.class == UnsupportedVersionException
+        e.message == "The specified Gradle distribution '${dist.toURI()}' is not supported by this tooling API version (${GradleVersion.current().version}, protocol version 4)"
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiModelIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiModelIntegrationTest.groovy
new file mode 100644
index 0000000..1c9a191
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiModelIntegrationTest.groovy
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.tooling
+
+import org.gradle.tooling.BuildException
+import org.gradle.tooling.model.Project
+import org.gradle.tooling.ProgressListener
+
+class ToolingApiModelIntegrationTest extends ToolingApiSpecification {
+    def "receives progress and logging while the model is building"() {
+        dist.testFile('build.gradle') << '''
+System.out.println 'this is stdout'
+System.err.println 'this is stderr'
+'''
+
+        def stdout = new ByteArrayOutputStream()
+        def stderr = new ByteArrayOutputStream()
+        def progressMessages = []
+
+        when:
+        withConnection { connection ->
+            def model = connection.model(Project.class)
+            model.standardOutput = stdout
+            model.standardError = stderr
+            model.addProgressListener({ event -> progressMessages << event.description } as ProgressListener)
+            return model.get()
+        }
+
+        then:
+        stdout.toString().contains('this is stdout')
+        stderr.toString().contains('this is stderr')
+        progressMessages.size() >= 2
+        progressMessages.pop() == ''
+        progressMessages.every { it }
+    }
+
+    def "tooling api reports failure to build model"() {
+        dist.testFile('build.gradle') << 'broken'
+
+        when:
+        withConnection { connection ->
+            return connection.getModel(Project.class)
+        }
+
+        then:
+        BuildException e = thrown()
+        e.message.startsWith('Could not fetch model of type \'Project\' using Gradle')
+        e.cause.message.contains('A problem occurred evaluating root project')
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiSpecification.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiSpecification.groovy
new file mode 100644
index 0000000..c5430f1
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiSpecification.groovy
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.tooling
+
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.util.SetSystemProperties
+import org.junit.Rule
+import spock.lang.Specification
+
+abstract class ToolingApiSpecification extends Specification {
+    @Rule public final SetSystemProperties sysProperties = new SetSystemProperties()
+    @Rule public final GradleDistribution dist = new GradleDistribution()
+    @Rule public final ToolingApi toolingApi = new ToolingApi()
+
+    def withConnection(Closure cl) {
+        toolingApi.withConnection(cl)
+    }
+
+    def maybeFailWithConnection(Closure cl) {
+        toolingApi.maybeFailWithConnection(cl)
+    }
+}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectA-1.2-ivy.xml b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectA-1.2-ivy.xml
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectA-1.2-ivy.xml
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectA-1.2-ivy.xml
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectB-1.5-ivy.xml b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectB-1.5-ivy.xml
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectB-1.5-ivy.xml
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectB-1.5-ivy.xml
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectWithConfigurationHierarchy.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectWithConfigurationHierarchy.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectWithConfigurationHierarchy.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectWithConfigurationHierarchy.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/projectA-1.2-ivy.xml b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/projectA-1.2-ivy.xml
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/projectA-1.2-ivy.xml
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/projectA-1.2-ivy.xml
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/projectB-1.5-ivy.xml b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/projectB-1.5-ivy.xml
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/projectB-1.5-ivy.xml
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/projectB-1.5-ivy.xml
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/projectWithCyclesInDependencyGraph.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/projectWithCyclesInDependencyGraph.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/projectWithCyclesInDependencyGraph.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/projectWithCyclesInDependencyGraph.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveCycleInProjectDependencies/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveCycleInProjectDependencies/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveCycleInProjectDependencies/build.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveCycleInProjectDependencies/build.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveCycleInProjectDependencies/settings.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveCycleInProjectDependencies/settings.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveCycleInProjectDependencies/settings.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveCycleInProjectDependencies/settings.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canNestModules/projectWithNestedModules.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canNestModules/projectWithNestedModules.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canNestModules/projectWithNestedModules.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canNestModules/projectWithNestedModules.gradle
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canResolveDependenciesFromAFlatDir/projectWithFlatDir.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canResolveDependenciesFromAFlatDir/projectWithFlatDir.gradle
new file mode 100644
index 0000000..165ec86
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canResolveDependenciesFromAFlatDir/projectWithFlatDir.gradle
@@ -0,0 +1,28 @@
+defaultTasks 'list'
+
+repositories {
+    flatDir dirs: file('repo')
+}
+
+configurations {
+    compile
+}
+
+dependencies {
+    compile 'group:a:1.4'
+    compile 'group:b:2.0'
+    compile 'group:c:'
+}
+
+task list << {
+    def a = file('repo/a-1.4.jar')
+    def b = file('repo/b.jar')
+    def c = file('repo/c.jar')
+    a.parentFile.mkdirs()
+    a << 'content'
+    b << 'content'
+    c << 'content'
+
+    def files = configurations.compile.files
+    assert files == [a, b, c] as Set
+}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/projectA-1.2-ivy.xml b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/projectA-1.2-ivy.xml
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/projectA-1.2-ivy.xml
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/projectA-1.2-ivy.xml
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/projectB-1.5-ivy.xml b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/projectB-1.5-ivy.xml
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/projectB-1.5-ivy.xml
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/projectB-1.5-ivy.xml
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/projectWithDynamicVersions.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/projectWithDynamicVersions.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/projectWithDynamicVersions.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/projectWithDynamicVersions.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectA-1.2-ivy.xml b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectA-1.2-ivy.xml
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectA-1.2-ivy.xml
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectA-1.2-ivy.xml
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectA-2.0-ivy.xml b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectA-2.0-ivy.xml
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectA-2.0-ivy.xml
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectA-2.0-ivy.xml
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectB-1.5-ivy.xml b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectB-1.5-ivy.xml
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectB-1.5-ivy.xml
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectB-1.5-ivy.xml
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectB-2.1.5-ivy.xml b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectB-2.1.5-ivy.xml
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectB-2.1.5-ivy.xml
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectB-2.1.5-ivy.xml
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectWithConflicts.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectWithConflicts.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectWithConflicts.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectWithConflicts.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/settings.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/settings.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/settings.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/settings.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/reportsUnknownDependencyError/projectWithUnknownDependency.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/reportsUnknownDependencyError/projectWithUnknownDependency.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/reportsUnknownDependencyError/projectWithUnknownDependency.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/reportsUnknownDependencyError/projectWithUnknownDependency.gradle
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/CommandLineIntegrationTest/shared/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/CommandLineIntegrationTest/shared/build.gradle
new file mode 100644
index 0000000..e2386c8
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/CommandLineIntegrationTest/shared/build.gradle
@@ -0,0 +1,20 @@
+
+task checkGradleUserHomeViaSystemEnv << {
+    assert gradle.gradleUserHomeDir == file('customUserHome')
+}
+
+task checkDefaultGradleUserHome<< {
+    assert gradle.gradleUserHomeDir == new File(System.properties['user.home'], ".gradle")
+}
+
+task checkSystemPropertyGradleUserHomeHasPrecedence << {
+    assert gradle.gradleUserHomeDir == file('systemPropCustomUserHome')
+}
+
+task checkJavaHome << {
+    assert Jvm.current().javaHome == file(expectedJavaHome)
+}
+
+task checkSystemProperty << {
+    assert System.getProperty('customSystemProperty') == 'custom-value'
+}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/CommandLineIntegrationTest/shared/settings.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/CommandLineIntegrationTest/shared/settings.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/CommandLineIntegrationTest/shared/settings.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/CommandLineIntegrationTest/shared/settings.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/CrossVersionCompatibilityIntegrationTest/canBuildJavaProject/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/CrossVersionCompatibilityIntegrationTest/canBuildJavaProject/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/CrossVersionCompatibilityIntegrationTest/canBuildJavaProject/build.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/CrossVersionCompatibilityIntegrationTest/canBuildJavaProject/build.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/CrossVersionCompatibilityIntegrationTest/canBuildJavaProject/buildSrc/src/main/groovy/org/gradle/CustomTask.groovy b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/CrossVersionCompatibilityIntegrationTest/canBuildJavaProject/buildSrc/src/main/groovy/org/gradle/CustomTask.groovy
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/CrossVersionCompatibilityIntegrationTest/canBuildJavaProject/buildSrc/src/main/groovy/org/gradle/CustomTask.groovy
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/CrossVersionCompatibilityIntegrationTest/canBuildJavaProject/buildSrc/src/main/groovy/org/gradle/CustomTask.groovy
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/CrossVersionCompatibilityIntegrationTest/canBuildJavaProject/src/main/java/org/gradle/Person.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/CrossVersionCompatibilityIntegrationTest/canBuildJavaProject/src/main/java/org/gradle/Person.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/CrossVersionCompatibilityIntegrationTest/canBuildJavaProject/src/main/java/org/gradle/Person.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/CrossVersionCompatibilityIntegrationTest/canBuildJavaProject/src/main/java/org/gradle/Person.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/CrossVersionCompatibilityIntegrationTest/shared/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/CrossVersionCompatibilityIntegrationTest/shared/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/CrossVersionCompatibilityIntegrationTest/shared/build.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/CrossVersionCompatibilityIntegrationTest/shared/build.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ExecIntegrationTest/canExecuteCommands/canExecuteCommands.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ExecIntegrationTest/canExecuteCommands/canExecuteCommands.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/ExecIntegrationTest/canExecuteCommands/canExecuteCommands.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ExecIntegrationTest/canExecuteCommands/canExecuteCommands.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ExecIntegrationTest/canExecuteJava/canExecuteJava.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ExecIntegrationTest/canExecuteJava/canExecuteJava.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/ExecIntegrationTest/canExecuteJava/canExecuteJava.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ExecIntegrationTest/canExecuteJava/canExecuteJava.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ExecIntegrationTest/shared/src/main/java/org/gradle/TestMain.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ExecIntegrationTest/shared/src/main/java/org/gradle/TestMain.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/ExecIntegrationTest/shared/src/main/java/org/gradle/TestMain.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ExecIntegrationTest/shared/src/main/java/org/gradle/TestMain.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalBuildIntegrationTest/shared/buildSrc/src/main/java/DirTransformerTask.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalBuildIntegrationTest/shared/buildSrc/src/main/java/DirTransformerTask.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalBuildIntegrationTest/shared/buildSrc/src/main/java/DirTransformerTask.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalBuildIntegrationTest/shared/buildSrc/src/main/java/DirTransformerTask.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalBuildIntegrationTest/shared/buildSrc/src/main/java/GeneratorTask.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalBuildIntegrationTest/shared/buildSrc/src/main/java/GeneratorTask.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalBuildIntegrationTest/shared/buildSrc/src/main/java/GeneratorTask.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalBuildIntegrationTest/shared/buildSrc/src/main/java/GeneratorTask.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalBuildIntegrationTest/shared/buildSrc/src/main/java/TransformerTask.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalBuildIntegrationTest/shared/buildSrc/src/main/java/TransformerTask.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalBuildIntegrationTest/shared/buildSrc/src/main/java/TransformerTask.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalBuildIntegrationTest/shared/buildSrc/src/main/java/TransformerTask.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/NewIPerson.groovy b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/NewIPerson.groovy
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/NewIPerson.groovy
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/NewIPerson.groovy
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/build.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/build.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/src/main/groovy/IPerson.groovy b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/src/main/groovy/IPerson.groovy
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/src/main/groovy/IPerson.groovy
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/src/main/groovy/IPerson.groovy
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/src/main/groovy/Person.groovy b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/src/main/groovy/Person.groovy
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/src/main/groovy/Person.groovy
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/src/main/groovy/Person.groovy
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesSourceWhenPropertiesChange/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesSourceWhenPropertiesChange/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesSourceWhenPropertiesChange/build.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesSourceWhenPropertiesChange/build.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/groovy/Person.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/groovy/Person.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/groovy/Person.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/groovy/Person.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/groovy/PersonImpl.Groovy b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/groovy/PersonImpl.Groovy
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/groovy/PersonImpl.Groovy
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalGroovyCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/groovy/PersonImpl.Groovy
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClasses/NewIPerson.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClasses/NewIPerson.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClasses/NewIPerson.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClasses/NewIPerson.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClasses/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClasses/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClasses/build.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClasses/build.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClasses/src/main/java/IPerson.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClasses/src/main/java/IPerson.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClasses/src/main/java/IPerson.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClasses/src/main/java/IPerson.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClasses/src/main/java/Person.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClasses/src/main/java/Person.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClasses/src/main/java/Person.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClasses/src/main/java/Person.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClassesAcrossProjectBoundaries/NewIPerson.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClassesAcrossProjectBoundaries/NewIPerson.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClassesAcrossProjectBoundaries/NewIPerson.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClassesAcrossProjectBoundaries/NewIPerson.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClassesAcrossProjectBoundaries/app/src/main/java/Person.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClassesAcrossProjectBoundaries/app/src/main/java/Person.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClassesAcrossProjectBoundaries/app/src/main/java/Person.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClassesAcrossProjectBoundaries/app/src/main/java/Person.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClassesAcrossProjectBoundaries/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClassesAcrossProjectBoundaries/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClassesAcrossProjectBoundaries/build.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClassesAcrossProjectBoundaries/build.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClassesAcrossProjectBoundaries/lib/src/main/java/IPerson.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClassesAcrossProjectBoundaries/lib/src/main/java/IPerson.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClassesAcrossProjectBoundaries/lib/src/main/java/IPerson.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClassesAcrossProjectBoundaries/lib/src/main/java/IPerson.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClassesAcrossProjectBoundaries/settings.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClassesAcrossProjectBoundaries/settings.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClassesAcrossProjectBoundaries/settings.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesDependentClassesAcrossProjectBoundaries/settings.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/build.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/build.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/java/Test.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/java/Test.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/java/Test.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalJavaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/java/Test.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesDependentClasses/NewIPerson.scala b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesDependentClasses/NewIPerson.scala
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesDependentClasses/NewIPerson.scala
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesDependentClasses/NewIPerson.scala
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesDependentClasses/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesDependentClasses/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesDependentClasses/build.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesDependentClasses/build.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesDependentClasses/src/main/scala/IPerson.scala b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesDependentClasses/src/main/scala/IPerson.scala
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesDependentClasses/src/main/scala/IPerson.scala
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesDependentClasses/src/main/scala/IPerson.scala
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesDependentClasses/src/main/scala/Person.scala b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesDependentClasses/src/main/scala/Person.scala
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesDependentClasses/src/main/scala/Person.scala
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesDependentClasses/src/main/scala/Person.scala
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/build.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/build.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/scala/Person.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/scala/Person.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/scala/Person.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/scala/Person.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/scala/PersonImpl.scala b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/scala/PersonImpl.scala
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/scala/PersonImpl.scala
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalScalaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/scala/PersonImpl.scala
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/doesNotRunStaleTests/src/test/java/Broken.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/doesNotRunStaleTests/src/test/java/Broken.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/doesNotRunStaleTests/src/test/java/Broken.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/doesNotRunStaleTests/src/test/java/Broken.java
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSelectedTestsChange/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSelectedTestsChange/build.gradle
new file mode 100644
index 0000000..9ec301a
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSelectedTestsChange/build.gradle
@@ -0,0 +1,14 @@
+apply plugin: 'java'
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    testCompile 'junit:junit:4.8.2'
+    testCompile 'org.testng:testng:5.14.10'
+}
+
+test {
+    include '**/*Test.*'
+}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSelectedTestsChange/src/test/java/JUnitExtra.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSelectedTestsChange/src/test/java/JUnitExtra.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSelectedTestsChange/src/test/java/JUnitExtra.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSelectedTestsChange/src/test/java/JUnitExtra.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSelectedTestsChange/src/test/java/JUnitTest.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSelectedTestsChange/src/test/java/JUnitTest.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSelectedTestsChange/src/test/java/JUnitTest.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSelectedTestsChange/src/test/java/JUnitTest.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSelectedTestsChange/src/test/java/TestNGTest.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSelectedTestsChange/src/test/java/TestNGTest.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSelectedTestsChange/src/test/java/TestNGTest.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSelectedTestsChange/src/test/java/TestNGTest.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSourceChanges/NewMainClass.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSourceChanges/NewMainClass.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSourceChanges/NewMainClass.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSourceChanges/NewMainClass.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSourceChanges/NewOk.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSourceChanges/NewOk.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSourceChanges/NewOk.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSourceChanges/NewOk.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSourceChanges/src/main/java/MainClass.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSourceChanges/src/main/java/MainClass.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSourceChanges/src/main/java/MainClass.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSourceChanges/src/main/java/MainClass.java
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/shared/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/shared/build.gradle
new file mode 100644
index 0000000..6058be2
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/shared/build.gradle
@@ -0,0 +1,9 @@
+apply plugin: 'java'
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    testCompile 'junit:junit:4.8.2'
+}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/shared/src/test/java/Ok.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/shared/src/test/java/Ok.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/shared/src/test/java/Ok.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/shared/src/test/java/Ok.java
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/build.gradle
new file mode 100644
index 0000000..f8f5d65
--- /dev/null
+++ b/subprojects/integ-test/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.2'
+}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/src/test/java/org/gradle/Test1.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/src/test/java/org/gradle/Test1.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/src/test/java/org/gradle/Test1.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/src/test/java/org/gradle/Test1.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/src/test/java/org/gradle/Test2.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/src/test/java/org/gradle/Test2.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/src/test/java/org/gradle/Test2.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/src/test/java/org/gradle/Test2.java
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canRunSingleTests/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canRunSingleTests/build.gradle
new file mode 100644
index 0000000..fd5d6b1
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canRunSingleTests/build.gradle
@@ -0,0 +1,9 @@
+apply plugin: 'java'
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    testCompile 'junit:junit:4.8.2'
+}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canRunSingleTests/src/test/java/NotATest.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canRunSingleTests/src/test/java/NotATest.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canRunSingleTests/src/test/java/NotATest.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canRunSingleTests/src/test/java/NotATest.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canRunSingleTests/src/test/java/Ok.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canRunSingleTests/src/test/java/Ok.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canRunSingleTests/src/test/java/Ok.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canRunSingleTests/src/test/java/Ok.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canRunSingleTests/src/test/java/Ok2.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canRunSingleTests/src/test/java/Ok2.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canRunSingleTests/src/test/java/Ok2.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canRunSingleTests/src/test/java/Ok2.java
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/detectsTestClasses/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/detectsTestClasses/build.gradle
new file mode 100644
index 0000000..ed55229
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/detectsTestClasses/build.gradle
@@ -0,0 +1,8 @@
+apply plugin: 'java'
+
+repositories {
+    mavenCentral()
+}
+dependencies {
+    compile 'junit:junit:4.8.2'
+}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/AbstractHasRunWith.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/AbstractHasRunWith.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/AbstractHasRunWith.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/AbstractHasRunWith.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/CustomRunner.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/CustomRunner.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/CustomRunner.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/CustomRunner.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/EmptyRunWithSubclass.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/EmptyRunWithSubclass.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/EmptyRunWithSubclass.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/EmptyRunWithSubclass.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/TestsOnInner.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/TestsOnInner.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/TestsOnInner.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/TestsOnInner.java
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/executesTestsInCorrectEnvironment/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/executesTestsInCorrectEnvironment/build.gradle
new file mode 100644
index 0000000..92b1017
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/executesTestsInCorrectEnvironment/build.gradle
@@ -0,0 +1,8 @@
+apply plugin: 'java'
+repositories { mavenCentral() }
+dependencies { testCompile 'junit:junit:4.8.2', 'ant:ant:1.6.1', 'ant:ant-launcher:1.6.1' }
+test {
+    systemProperties.testSysProperty = 'value'
+    systemProperties.projectDir = projectDir
+    environment.TEST_ENV_VAR = 'value'
+}
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OkTest.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OkTest.java
new file mode 100644
index 0000000..365d645
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OkTest.java
@@ -0,0 +1,72 @@
+package org.gradle;
+
+import static org.junit.Assert.*;
+
+import java.io.PrintStream;
+import java.util.logging.Logger;
+
+public class OkTest {
+    static {
+        System.out.println("class loaded");
+    }
+
+    public OkTest() {
+        System.out.println("test constructed");
+    }
+
+    @org.junit.Test
+    public void ok() throws Exception {
+        // check JUnit version
+        assertEquals("4.8.2", new org.junit.runner.JUnitCore().getVersion());
+        // check Ant version
+        assertTrue(org.apache.tools.ant.Main.getAntVersion().contains("1.6.1"));
+        // check working dir
+        assertEquals(System.getProperty("projectDir"), System.getProperty("user.dir"));
+        // check classloader
+        assertSame(ClassLoader.getSystemClassLoader(), getClass().getClassLoader());
+        assertSame(getClass().getClassLoader(), Thread.currentThread().getContextClassLoader());
+        // check Gradle and impl classes not visible
+        try {
+            getClass().getClassLoader().loadClass("org.gradle.api.Project");
+            fail();
+        } catch (ClassNotFoundException e) {
+        }
+        try {
+            getClass().getClassLoader().loadClass("org.slf4j.Logger");
+            fail();
+        } catch (ClassNotFoundException e) {
+        }
+        // check sys properties
+        assertEquals("value", System.getProperty("testSysProperty"));
+        // check env vars
+        assertEquals("value", System.getenv("TEST_ENV_VAR"));
+
+        // check stdout and stderr and logging
+        System.out.println("This is test stdout");
+        System.out.print("no EOL");
+        System.out.println();
+        System.err.println("This is test stderr");
+        Logger.getLogger("test-logger").warning("this is a warning");
+
+        final PrintStream out = System.out;
+        // logging from a shutdown hook
+        Runtime.getRuntime().addShutdownHook(new Thread() {
+            @Override
+            public void run() {
+                out.println("stdout from a shutdown hook.");
+                Logger.getLogger("test-logger").info("info from a shutdown hook.");
+            }
+        });
+
+        // logging from another thread
+        Thread thread = new Thread() {
+            @Override
+            public void run() {
+                System.out.println("stdout from another thread");
+                Logger.getLogger("test-logger").info("info from another thread.");
+            }
+        };
+        thread.start();
+        thread.join();
+    }
+}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OtherTest.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OtherTest.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OtherTest.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OtherTest.java
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/junit3Tests/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/junit3Tests/build.gradle
new file mode 100644
index 0000000..0f93806
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/junit3Tests/build.gradle
@@ -0,0 +1,9 @@
+apply plugin: 'java'
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    testCompile "junit:junit:3.8"
+}
\ No newline at end of file
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/junit3Tests/src/test/java/org/gradle/Junit3Test.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/junit3Tests/src/test/java/org/gradle/Junit3Test.java
new file mode 100644
index 0000000..0c96d77
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/junit3Tests/src/test/java/org/gradle/Junit3Test.java
@@ -0,0 +1,9 @@
+package org.gradle;
+
+import junit.framework.TestCase;
+
+public class Junit3Test extends TestCase {
+    public void testRenamesItself() {
+        setName("a test that renames itself");
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/junit4Tests/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/junit4Tests/build.gradle
new file mode 100644
index 0000000..554315f
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/junit4Tests/build.gradle
@@ -0,0 +1,9 @@
+apply plugin: 'java'
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    testCompile "junit:junit:4.8.2"
+}
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/junit4Tests/src/test/java/org/gradle/IgnoredTest.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/junit4Tests/src/test/java/org/gradle/IgnoredTest.java
new file mode 100644
index 0000000..ec02a29
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/junit4Tests/src/test/java/org/gradle/IgnoredTest.java
@@ -0,0 +1,12 @@
+package org.gradle;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+ at Ignore
+public class IgnoredTest {
+    @Test
+    public void testIgnored() {
+        throw new RuntimeException();
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/junit4Tests/src/test/java/org/gradle/Junit4Test.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/junit4Tests/src/test/java/org/gradle/Junit4Test.java
new file mode 100644
index 0000000..b56604a
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/junit4Tests/src/test/java/org/gradle/Junit4Test.java
@@ -0,0 +1,15 @@
+package org.gradle;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class Junit4Test {
+    @Test
+    public void ok() {
+    }
+
+    @Test @Ignore
+    public void broken() {
+        throw new RuntimeException();
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/junit4_4Tests/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/junit4_4Tests/build.gradle
new file mode 100644
index 0000000..ac7b66a
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/junit4_4Tests/build.gradle
@@ -0,0 +1,9 @@
+apply plugin: 'java'
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    testCompile "junit:junit:4.4"
+}
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/build.gradle
new file mode 100644
index 0000000..a58531f
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/build.gradle
@@ -0,0 +1,3 @@
+apply plugin: 'java'
+repositories { mavenCentral() }
+dependencies { testCompile 'junit:junit:4.8.2' }
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenAfter.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenAfter.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenAfter.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenAfter.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenAfterClass.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenAfterClass.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenAfterClass.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenAfterClass.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenBefore.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenBefore.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenBefore.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenBefore.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenBeforeAndAfter.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenBeforeAndAfter.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenBeforeAndAfter.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenBeforeAndAfter.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenBeforeClass.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenBeforeClass.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenBeforeClass.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenBeforeClass.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenConstructor.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenConstructor.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenConstructor.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenConstructor.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenException.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenException.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenException.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenException.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenTest.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenTest.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenTest.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenTest.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/Unloadable.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/Unloadable.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/Unloadable.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/Unloadable.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/build.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/build.gradle
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/buildSrc/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/buildSrc/build.gradle
new file mode 100644
index 0000000..e9238dc
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/buildSrc/build.gradle
@@ -0,0 +1,22 @@
+println 'main buildSrc quiet'
+logger.info 'main buildSrc info'
+
+task classes << {}
+
+convention.plugins.java = new ProjectInfo(project: project);
+
+class ProjectInfo implements org.gradle.api.internal.plugins.EmbeddableJavaProject {
+    def Project project
+
+    Collection<String> getRebuildTasks() {
+        return ['classes']
+    }
+
+    Collection<String> getBuildTasks() {
+        return ['classes']
+    }
+
+    FileCollection getRuntimeClasspath() {
+        return project.files()
+    }
+}
\ No newline at end of file
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/external.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/external.gradle
new file mode 100644
index 0000000..8c10975
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/external.gradle
@@ -0,0 +1,9 @@
+println 'external QUIET message'
+logging.captureStandardOutput LogLevel.INFO
+println 'external INFO message'
+
+System.err.println 'external ERROR error message'
+logging.captureStandardError LogLevel.LIFECYCLE
+System.err.println 'external LIFECYCLE error message'
+
+logger.lifecycle 'external LIFECYCLE log message'
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/init.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/init.gradle
new file mode 100644
index 0000000..f6042a0
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/init.gradle
@@ -0,0 +1,46 @@
+
+println 'init QUIET out'
+logging.captureStandardOutput LogLevel.INFO
+println 'init INFO out'
+
+System.err.println 'init ERROR err'
+logging.captureStandardError LogLevel.INFO
+System.err.println 'init INFO err'
+
+logger.lifecycle('init lifecycle log')
+logger.info('init info log')
+
+useLogger(new CustomLogger())
+
+class CustomLogger extends BuildAdapter implements BuildListener, ProjectEvaluationListener, TaskExecutionListener, TaskActionListener {
+    def logger = Logging.getLogger('init-script')
+
+    public void buildFinished(BuildResult result) {
+        logger.info("LOGGER: build finished")
+        println 'init callback quiet out'
+    }
+
+    public void beforeEvaluate(Project project) {
+        logger.lifecycle("LOGGER: evaluating $project.path")
+    }
+
+    public void afterEvaluate(Project project, ProjectState state) {
+        logger.info("LOGGER: evaluated project $project.path")
+    }
+
+    public void beforeExecute(Task task) {
+        logger.lifecycle("LOGGER: executing $task.path")
+    }
+
+    public void afterExecute(Task task, TaskState state) {
+        logger.info("LOGGER: executed task $task.path")
+    }
+
+    public void beforeActions(Task task) {
+        logger.info("LOGGER: task $task.path starting work")
+    }
+
+    public void afterActions(Task task) {
+        logger.info("LOGGER: task $task.path completed work")
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/nestedBuild/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/nestedBuild/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/nestedBuild/build.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/nestedBuild/build.gradle
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/nestedBuild/buildSrc/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/nestedBuild/buildSrc/build.gradle
new file mode 100644
index 0000000..807e0b2
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/nestedBuild/buildSrc/build.gradle
@@ -0,0 +1,22 @@
+println 'nestedBuild buildSrc quiet'
+logger.info 'nestedBuild buildSrc info'
+
+task classes << { }
+
+convention.plugins.java = new ProjectInfo(project: project);
+
+class ProjectInfo implements org.gradle.api.internal.plugins.EmbeddableJavaProject {
+    def Project project
+
+    Collection<String> getRebuildTasks() {
+        return ['classes']
+    }
+
+    Collection<String> getBuildTasks() {
+        return ['classes']
+    }
+
+    FileCollection getRuntimeClasspath() {
+        return project.files()
+    }
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/multiproject/useSubprojects/water/bluewhale/.gitignore b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/nestedBuild/settings.gradle
similarity index 100%
rename from subprojects/docs/src/samples/userguide/multiproject/useSubprojects/water/bluewhale/.gitignore
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/nestedBuild/settings.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/project1/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/project1/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/project1/build.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/project1/build.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/project2/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/project2/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/project2/build.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/project2/build.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/settings.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/settings.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/settings.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/settings.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/multiThreaded/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/multiThreaded/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/multiThreaded/build.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/multiThreaded/build.gradle
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ProjectLayoutIntegrationTest/canUseANonStandardBuildDir/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ProjectLayoutIntegrationTest/canUseANonStandardBuildDir/build.gradle
new file mode 100644
index 0000000..ad09095
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ProjectLayoutIntegrationTest/canUseANonStandardBuildDir/build.gradle
@@ -0,0 +1,13 @@
+apply plugin: 'java'
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    testCompile 'junit:junit:4.8.2'
+}
+
+buildDirName = 'target'
+sourceSets.main.classesDir = new File(buildDir, 'main-classes')
+sourceSets.test.classesDir = new File(buildDir, 'test-classes')
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ProjectLayoutIntegrationTest/canUseANonStandardBuildDir/src/main/java/Person.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ProjectLayoutIntegrationTest/canUseANonStandardBuildDir/src/main/java/Person.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/ProjectLayoutIntegrationTest/canUseANonStandardBuildDir/src/main/java/Person.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ProjectLayoutIntegrationTest/canUseANonStandardBuildDir/src/main/java/Person.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/ProjectLayoutIntegrationTest/canUseANonStandardBuildDir/src/test/java/PersonTest.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ProjectLayoutIntegrationTest/canUseANonStandardBuildDir/src/test/java/PersonTest.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/ProjectLayoutIntegrationTest/canUseANonStandardBuildDir/src/test/java/PersonTest.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ProjectLayoutIntegrationTest/canUseANonStandardBuildDir/src/test/java/PersonTest.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/ignore/bad.file b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/ignore/bad.file
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/ignore/bad.file
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/ignore/bad.file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/one.a b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/one.a
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/one.a
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/one.a
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/one.b b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/one.b
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/one.b
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/one.b
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/sub/ignore/bad.file b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/sub/ignore/bad.file
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/sub/ignore/bad.file
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/sub/ignore/bad.file
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/sub/onesub.a b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/sub/onesub.a
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/sub/onesub.a
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/sub/onesub.a
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/sub/onesub.b b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/sub/onesub.b
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/sub/onesub.b
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src/one/sub/onesub.b
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/root.a b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src/root.a
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/root.a
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src/root.a
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/root.b b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src/root.b
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/root.b
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src/root.b
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/two/ignore/bad.file b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src/two/ignore/bad.file
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/two/ignore/bad.file
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src/two/ignore/bad.file
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src/two/two.a b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src/two/two.a
new file mode 100644
index 0000000..fcc157b
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src/two/two.a
@@ -0,0 +1,3 @@
+${one}
+${one+1}
+${one+1+1}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/two/two.b b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src/two/two.b
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src/two/two.b
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src/two/two.b
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src2/three/three.a b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src2/three/three.a
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src2/three/three.a
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src2/three/three.a
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src2/three/three.b b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src2/three/three.b
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/copyTestResources/src2/three/three.b
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/copyTestResources/src2/three/three.b
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/eclipseproject/groovy/expectedClasspathFile.txt b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/eclipseproject/groovy/expectedClasspathFile.txt
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/eclipseproject/groovy/expectedClasspathFile.txt
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/eclipseproject/groovy/expectedClasspathFile.txt
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/eclipseproject/groovy/expectedProjectFile.txt b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/eclipseproject/groovy/expectedProjectFile.txt
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/eclipseproject/groovy/expectedProjectFile.txt
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/eclipseproject/groovy/expectedProjectFile.txt
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/eclipseproject/java/expectedApiClasspathFile.txt b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/eclipseproject/java/expectedApiClasspathFile.txt
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/eclipseproject/java/expectedApiClasspathFile.txt
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/eclipseproject/java/expectedApiClasspathFile.txt
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/eclipseproject/java/expectedApiProjectFile.txt b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/eclipseproject/java/expectedApiProjectFile.txt
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/eclipseproject/java/expectedApiProjectFile.txt
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/eclipseproject/java/expectedApiProjectFile.txt
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/eclipseproject/java/expectedWebserviceClasspathFile.txt b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/eclipseproject/java/expectedWebserviceClasspathFile.txt
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/eclipseproject/java/expectedWebserviceClasspathFile.txt
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/eclipseproject/java/expectedWebserviceClasspathFile.txt
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/eclipseproject/java/expectedWebserviceProjectFile.txt b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/eclipseproject/java/expectedWebserviceProjectFile.txt
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/eclipseproject/java/expectedWebserviceProjectFile.txt
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/eclipseproject/java/expectedWebserviceProjectFile.txt
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/eclipseproject/java/expectedWebserviceWtpFile.txt b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/eclipseproject/java/expectedWebserviceWtpFile.txt
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/eclipseproject/java/expectedWebserviceWtpFile.txt
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/eclipseproject/java/expectedWebserviceWtpFile.txt
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/eclipseproject/scala/expectedClasspathFile.txt b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/eclipseproject/scala/expectedClasspathFile.txt
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/eclipseproject/scala/expectedClasspathFile.txt
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/eclipseproject/scala/expectedClasspathFile.txt
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/eclipseproject/scala/expectedProjectFile.txt b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/eclipseproject/scala/expectedProjectFile.txt
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/eclipseproject/scala/expectedProjectFile.txt
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/eclipseproject/scala/expectedProjectFile.txt
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithDependencyInMappedAndUnMappedConfiguration/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithDependencyInMappedAndUnMappedConfiguration/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithDependencyInMappedAndUnMappedConfiguration/build.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithDependencyInMappedAndUnMappedConfiguration/build.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/overwritesExistingDependencies/settings.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithDependencyInMappedAndUnMappedConfiguration/settings.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/overwritesExistingDependencies/settings.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithDependencyInMappedAndUnMappedConfiguration/settings.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithMetadataArtifacts/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithMetadataArtifacts/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithMetadataArtifacts/build.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithMetadataArtifacts/build.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithDependencyInMappedAndUnMappedConfiguration/settings.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithMetadataArtifacts/settings.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithDependencyInMappedAndUnMappedConfiguration/settings.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithMetadataArtifacts/settings.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithNoMainArtifact/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithNoMainArtifact/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithNoMainArtifact/build.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithNoMainArtifact/build.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithMetadataArtifacts/settings.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithNoMainArtifact/settings.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithMetadataArtifacts/settings.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/maven/MavenProjectIntegrationTest/canDeployAProjectWithNoMainArtifact/settings.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/maven/MavenSnapshotIntegrationTest/shared/producer.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/maven/MavenSnapshotIntegrationTest/shared/producer.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/maven/MavenSnapshotIntegrationTest/shared/producer.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/maven/MavenSnapshotIntegrationTest/shared/producer.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/maven/MavenSnapshotIntegrationTest/shared/projectWithMavenSnapshots.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/maven/MavenSnapshotIntegrationTest/shared/projectWithMavenSnapshots.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/maven/MavenSnapshotIntegrationTest/shared/projectWithMavenSnapshots.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/maven/MavenSnapshotIntegrationTest/shared/projectWithMavenSnapshots.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/maven/MavenSnapshotIntegrationTest/shared/src/main/java/org/gradle/Test.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/maven/MavenSnapshotIntegrationTest/shared/src/main/java/org/gradle/Test.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/maven/MavenSnapshotIntegrationTest/shared/src/main/java/org/gradle/Test.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/maven/MavenSnapshotIntegrationTest/shared/src/main/java/org/gradle/Test.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/maven/pomGeneration/expectedNewPom.txt b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/maven/pomGeneration/expectedNewPom.txt
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/maven/pomGeneration/expectedNewPom.txt
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/maven/pomGeneration/expectedNewPom.txt
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/maven/pomGeneration/expectedPom.txt b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/maven/pomGeneration/expectedPom.txt
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/maven/pomGeneration/expectedPom.txt
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/maven/pomGeneration/expectedPom.txt
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/maven/pomGeneration/expectedQuickstartPom.txt b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/maven/pomGeneration/expectedQuickstartPom.txt
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/maven/pomGeneration/expectedQuickstartPom.txt
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/maven/pomGeneration/expectedQuickstartPom.txt
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/canListenForTestResults/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/canListenForTestResults/build.gradle
new file mode 100644
index 0000000..0e8aeb2
--- /dev/null
+++ b/subprojects/integ-test/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.14.10' }
+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.exception]" }
+}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/canListenForTestResults/src/test/java/AppException.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/canListenForTestResults/src/test/java/AppException.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/canListenForTestResults/src/test/java/AppException.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/canListenForTestResults/src/test/java/AppException.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/canListenForTestResults/src/test/java/SomeTest.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/canListenForTestResults/src/test/java/SomeTest.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/canListenForTestResults/src/test/java/SomeTest.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/canListenForTestResults/src/test/java/SomeTest.java
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/executesTestsInCorrectEnvironment/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/executesTestsInCorrectEnvironment/build.gradle
new file mode 100644
index 0000000..ba38fcf
--- /dev/null
+++ b/subprojects/integ-test/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.14.10' }
+test {
+    useTestNG()
+    systemProperties.testSysProperty = 'value'
+    systemProperties.testDir = projectDir
+    environment.TEST_ENV_VAR = 'value'
+}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OkTest.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OkTest.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OkTest.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OkTest.java
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Failing/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Failing/build.gradle
new file mode 100644
index 0000000..48b3b43
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Failing/build.gradle
@@ -0,0 +1,16 @@
+apply plugin: 'groovy'
+
+sourceCompatibility=1.5
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+	groovy "org.codehaus.groovy:groovy-all:1.7.10"
+    testCompile 'org.testng:testng:5.14.10'
+}
+
+test {
+   useTestNG() 
+}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Failing/src/main/groovy/org/gradle/Ok.groovy b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Failing/src/main/groovy/org/gradle/Ok.groovy
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Failing/src/main/groovy/org/gradle/Ok.groovy
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Failing/src/main/groovy/org/gradle/Ok.groovy
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Failing/src/test/groovy/org/gradle/BadTest.groovy b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Failing/src/test/groovy/org/gradle/BadTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Failing/src/test/groovy/org/gradle/BadTest.groovy
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Failing/src/test/groovy/org/gradle/BadTest.groovy
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Passing/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Passing/build.gradle
new file mode 100644
index 0000000..48b3b43
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Passing/build.gradle
@@ -0,0 +1,16 @@
+apply plugin: 'groovy'
+
+sourceCompatibility=1.5
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+	groovy "org.codehaus.groovy:groovy-all:1.7.10"
+    testCompile 'org.testng:testng:5.14.10'
+}
+
+test {
+   useTestNG() 
+}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Passing/src/main/groovy/org/gradle/Ok.groovy b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Passing/src/main/groovy/org/gradle/Ok.groovy
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Passing/src/main/groovy/org/gradle/Ok.groovy
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Passing/src/main/groovy/org/gradle/Ok.groovy
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Passing/src/test/groovy/org/gradle/OkTest.groovy b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Passing/src/test/groovy/org/gradle/OkTest.groovy
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Passing/src/test/groovy/org/gradle/OkTest.groovy
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Passing/src/test/groovy/org/gradle/OkTest.groovy
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk14Failing/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk14Failing/build.gradle
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk14Failing/build.gradle
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk14Failing/build.gradle
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk14Failing/src/main/java/org/gradle/Ok.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk14Failing/src/main/java/org/gradle/Ok.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk14Failing/src/main/java/org/gradle/Ok.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk14Failing/src/main/java/org/gradle/Ok.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk14Failing/src/test/java/org/gradle/BadTest.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk14Failing/src/test/java/org/gradle/BadTest.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk14Failing/src/test/java/org/gradle/BadTest.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk14Failing/src/test/java/org/gradle/BadTest.java
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/build.gradle
new file mode 100644
index 0000000..98cc7ff
--- /dev/null
+++ b/subprojects/integ-test/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.14.10'
+}
+
+test {
+   useTestNG()
+}
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/main/java/org/gradle/Ok.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/main/java/org/gradle/Ok.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/main/java/org/gradle/Ok.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/main/java/org/gradle/Ok.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/BadTest.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/BadTest.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/BadTest.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/BadTest.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/BrokenAfterSuite.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/BrokenAfterSuite.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/BrokenAfterSuite.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/BrokenAfterSuite.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/TestWithBrokenMethodDependency.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/TestWithBrokenMethodDependency.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/TestWithBrokenMethodDependency.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/TestWithBrokenMethodDependency.java
diff --git a/subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/TestWithBrokenSetup.java b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/TestWithBrokenSetup.java
similarity index 100%
rename from subprojects/core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/TestWithBrokenSetup.java
rename to subprojects/integ-test/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/TestWithBrokenSetup.java
diff --git a/subprojects/jetty/src/main/resources/META-INF/gradle-plugins/jetty.properties b/subprojects/jetty/src/main/resources/META-INF/gradle-plugins/jetty.properties
index e6bbbdc..9b40710 100644
--- a/subprojects/jetty/src/main/resources/META-INF/gradle-plugins/jetty.properties
+++ b/subprojects/jetty/src/main/resources/META-INF/gradle-plugins/jetty.properties
@@ -1,16 +1 @@
-#
-# Copyright 2010 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-implementation-class=org.gradle.api.plugins.jetty.JettyPlugin
+implementation-class=org.gradle.api.plugins.jetty.JettyPlugin
diff --git a/subprojects/launcher/launcher.gradle b/subprojects/launcher/launcher.gradle
index 23ddf02..360eb45 100644
--- a/subprojects/launcher/launcher.gradle
+++ b/subprojects/launcher/launcher.gradle
@@ -5,6 +5,7 @@ dependencies {
 
     compile project(':core')
     compile project(':ui')
+    compile project(':toolingApi')
 
     compile libraries.slf4j_api
 
@@ -23,11 +24,12 @@ task startScripts << {
 }
 
 ideaModule {
-    whenConfigured { module ->
-        def runtimeProjects = rootProject.groovyProjects() - ([project] +
-                configurations.runtime.getAllDependencies(ProjectDependency).collect { it.dependencyProject })
-        runtimeProjects.each { groovyProject ->
-            module.dependencies.add(new org.gradle.plugins.idea.model.ModuleDependency(groovyProject.name, "RUNTIME"))
-        }
-    }
+    scopes.RUNTIME.plus << rootProject.configurations.runtime
+    scopes.RUNTIME.plus << rootProject.configurations.plugins
+    scopes.RUNTIME.plus << rootProject.configurations.coreImpl
+}
+eclipseClasspath {
+    plusConfigurations << rootProject.configurations.runtime
+    plusConfigurations << rootProject.configurations.plugins
+    plusConfigurations << rootProject.configurations.coreImpl
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/BuildActionParameters.java b/subprojects/launcher/src/main/java/org/gradle/launcher/BuildActionParameters.java
new file mode 100644
index 0000000..54fd1d2
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/BuildActionParameters.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.BuildClientMetaData;
+import org.gradle.initialization.BuildRequestMetaData;
+
+import java.io.Serializable;
+import java.util.Map;
+
+public interface BuildActionParameters extends Serializable {
+    BuildRequestMetaData getBuildRequestMetaData();
+
+    BuildClientMetaData getClientMetaData();
+
+    Map<String, String> getSystemProperties();
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/CommandLineActionFactory.java b/subprojects/launcher/src/main/java/org/gradle/launcher/CommandLineActionFactory.java
index d39a155..c53f1f8 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/CommandLineActionFactory.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/CommandLineActionFactory.java
@@ -15,6 +15,7 @@
  */
 package org.gradle.launcher;
 
+import org.gradle.BuildExceptionReporter;
 import org.gradle.CommandLineArgumentException;
 import org.gradle.StartParameter;
 import org.gradle.api.Action;
@@ -25,6 +26,7 @@ import org.gradle.initialization.*;
 import org.gradle.logging.LoggingConfiguration;
 import org.gradle.logging.LoggingManagerInternal;
 import org.gradle.logging.LoggingServiceRegistry;
+import org.gradle.logging.StyledTextOutputFactory;
 import org.gradle.logging.internal.OutputEventListener;
 import org.gradle.util.GradleVersion;
 
@@ -82,7 +84,13 @@ public class CommandLineActionFactory {
             action = new CommandLineParseFailureAction(parser, e);
         }
 
-        return new WithLoggingAction(loggingConfiguration, loggingServices, action);
+        return new WithLoggingAction(loggingConfiguration, loggingServices,
+                new ExceptionReportingAction(action,
+                        new BuildExceptionReporter(loggingServices.get(StyledTextOutputFactory.class), new StartParameter(), clientMetaData())));
+    }
+
+    private static GradleLauncherMetaData clientMetaData() {
+        return new GradleLauncherMetaData();
     }
 
     CommandLineConverter<StartParameter> createStartParameterConverter() {
@@ -90,7 +98,7 @@ public class CommandLineActionFactory {
     }
 
     ServiceRegistry createLoggingServices() {
-        return new LoggingServiceRegistry();
+        return LoggingServiceRegistry.newCommandLineProcessLogging();
     }
 
     private Action<ExecutionListener> createAction(CommandLineParser parser, final ParsedCommandLine commandLine, CommandLineConverter<StartParameter> startParameterConverter, final ServiceRegistry loggingServices) {
@@ -107,21 +115,23 @@ public class CommandLineActionFactory {
         StartParameter startParameter = new StartParameter();
         startParameterConverter.convert(commandLine, startParameter);
         DaemonConnector connector = new DaemonConnector(startParameter.getGradleUserHomeDir());
-        GradleLauncherMetaData clientMetaData = new GradleLauncherMetaData();
+        GradleLauncherMetaData clientMetaData = clientMetaData();
         long startTime = ManagementFactory.getRuntimeMXBean().getStartTime();
+        DaemonClient client = new DaemonClient(connector, clientMetaData, loggingServices.get(OutputEventListener.class));
 
         if (commandLine.hasOption(FOREGROUND)) {
             return new ActionAdapter(new DaemonMain(loggingServices, connector));
         }
         if (commandLine.hasOption(STOP)) {
-            return new StopDaemonAction(connector, loggingServices.get(OutputEventListener.class), clientMetaData);
+            return new ActionAdapter(new StopDaemonAction(client));
         }
 
         boolean useDaemon = System.getProperty("org.gradle.daemon", "false").equals("true");
         useDaemon = useDaemon || commandLine.hasOption(DAEMON);
         useDaemon = useDaemon && !commandLine.hasOption(NO_DAEMON);
         if (useDaemon) {
-            return new DaemonBuildAction(loggingServices.get(OutputEventListener.class), connector, commandLine, new File(System.getProperty("user.dir")), clientMetaData, startTime);
+            return new ActionAdapter(
+                    new DaemonBuildAction(client, commandLine, new File(System.getProperty("user.dir")), clientMetaData, startTime, System.getProperties()));
         }
 
         return new RunBuildAction(startParameter, loggingServices, new DefaultBuildRequestMetaData(clientMetaData, startTime));
@@ -130,7 +140,7 @@ public class CommandLineActionFactory {
     private static void showUsage(PrintStream out, CommandLineParser parser) {
         out.println();
         out.print("USAGE: ");
-        new GradleLauncherMetaData().describeCommand(out, "[option...]", "[task...]");
+        clientMetaData().describeCommand(out, "[option...]", "[task...]");
         out.println();
         out.println();
         parser.printUsage(out);
@@ -168,7 +178,7 @@ public class CommandLineActionFactory {
 
     private static class ShowVersionAction implements Runnable {
         public void run() {
-            System.out.println(new GradleVersion().prettyPrint());
+            System.out.println(GradleVersion.current().prettyPrint());
         }
     }
 
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/DaemonBuildAction.java b/subprojects/launcher/src/main/java/org/gradle/launcher/DaemonBuildAction.java
index 1933522..d921956 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/DaemonBuildAction.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/DaemonBuildAction.java
@@ -15,38 +15,33 @@
  */
 package org.gradle.launcher;
 
-import org.gradle.api.Action;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
 import org.gradle.initialization.BuildClientMetaData;
 import org.gradle.initialization.ParsedCommandLine;
-import org.gradle.launcher.protocol.Build;
-import org.gradle.logging.internal.OutputEventListener;
-import org.gradle.messaging.remote.internal.Connection;
+import org.gradle.util.GUtil;
 
 import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
 
-public class DaemonBuildAction extends DaemonClientAction implements Action<ExecutionListener> {
-    private static final Logger LOGGER = Logging.getLogger(DaemonBuildAction.class);
-    private final DaemonConnector connector;
+public class DaemonBuildAction implements Runnable {
+    private final DaemonClient client;
     private final ParsedCommandLine args;
     private final File currentDir;
     private final BuildClientMetaData clientMetaData;
     private final long startTime;
+    private final Map<String, String> systemProperties;
 
-    public DaemonBuildAction(OutputEventListener outputEventListener, DaemonConnector connector, ParsedCommandLine args, File currentDir, BuildClientMetaData clientMetaData, long startTime) {
-        super(outputEventListener);
-        this.connector = connector;
+    public DaemonBuildAction(DaemonClient client, ParsedCommandLine args, File currentDir, BuildClientMetaData clientMetaData, long startTime, Map<?, ?> systemProperties) {
+        this.client = client;
         this.args = args;
         this.currentDir = currentDir;
         this.clientMetaData = clientMetaData;
         this.startTime = startTime;
+        this.systemProperties = new HashMap<String, String>();
+        GUtil.addToMap(this.systemProperties, systemProperties);
     }
 
-    public void execute(ExecutionListener executionListener) {
-        LOGGER.warn("Note: the Gradle build daemon is an experimental feature.");
-        LOGGER.warn("As such, you may experience unexpected build failures. You may need to occasionally stop the daemon.");
-        Connection<Object> connection = connector.connect();
-        run(new Build(currentDir, args, startTime, clientMetaData), connection, executionListener);
+    public void run() {
+        client.execute(new ExecuteBuildAction(currentDir, args), new DefaultBuildActionParameters(clientMetaData, startTime, systemProperties));
     }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/DaemonClient.java b/subprojects/launcher/src/main/java/org/gradle/launcher/DaemonClient.java
new file mode 100644
index 0000000..754e1da
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/DaemonClient.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.initialization.BuildClientMetaData;
+import org.gradle.initialization.GradleLauncherAction;
+import org.gradle.launcher.protocol.*;
+import org.gradle.logging.internal.OutputEvent;
+import org.gradle.logging.internal.OutputEventListener;
+import org.gradle.messaging.remote.internal.Connection;
+
+/**
+ * The client piece of the build daemon.
+ *
+ * <p>Protocol is this:</p>
+ *
+ * <ol> <li>Client connects to the server.</li>
+ *
+ * <li>Client sends a {@link org.gradle.launcher.protocol.Command} message.</li>
+ *
+ * <li>Server sends zero or more {@link org.gradle.logging.internal.OutputEvent} messages. Note that the server may send output messages before it receives the command message. </li>
+ *
+ * <li>Server sends a {@link org.gradle.launcher.protocol.CommandComplete} message.</li>
+ *
+ * <li>Connection is closed.</li>
+ *
+ * </ol>
+ */
+public class DaemonClient implements GradleLauncherActionExecuter<BuildActionParameters> {
+    private static final Logger LOGGER = Logging.getLogger(DaemonClient.class);
+    private final DaemonConnector connector;
+    private final BuildClientMetaData clientMetaData;
+    private final OutputEventListener outputEventListener;
+
+    public DaemonClient(DaemonConnector connector, BuildClientMetaData clientMetaData, OutputEventListener outputEventListener) {
+        this.connector = connector;
+        this.clientMetaData = clientMetaData;
+        this.outputEventListener = outputEventListener;
+    }
+
+    /**
+     * Stops the daemon, if it is running.
+     */
+    public void stop() {
+        Connection<Object> connection = connector.maybeConnect();
+        if (connection == null) {
+            LOGGER.lifecycle("Gradle daemon is not running.");
+            return;
+        }
+        run(new Stop(clientMetaData), connection);
+        LOGGER.lifecycle("Gradle daemon stopped.");
+    }
+
+    /**
+     * Executes the given action in the daemon. The action and parameters must be serializable.
+     *
+     * @param action The action
+     * @throws ReportedException On failure, when the failure has already been logged/reported.
+     */
+    public <T> T execute(GradleLauncherAction<T> action, BuildActionParameters parameters) {
+        LOGGER.warn("Note: the Gradle build daemon is an experimental feature.");
+        LOGGER.warn("As such, you may experience unexpected build failures. You may need to occasionally stop the daemon.");
+        Connection<Object> connection = connector.connect();
+        Result result = (Result) run(new Build(action, parameters), connection);
+        return (T) result.getResult();
+    }
+
+    private CommandComplete run(Command command, Connection<Object> connection) {
+        try {
+            connection.dispatch(command);
+            while (true) {
+                Object object = connection.receive();
+                if (object instanceof CommandComplete) {
+                    CommandComplete commandComplete = (CommandComplete) object;
+                    if (commandComplete.getFailure() != null) {
+                        throw commandComplete.getFailure();
+                    }
+                    return commandComplete;
+                }
+                OutputEvent outputEvent = (OutputEvent) object;
+                outputEventListener.onOutput(outputEvent);
+            }
+        } finally {
+            connection.stop();
+        }
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/DaemonClientAction.java b/subprojects/launcher/src/main/java/org/gradle/launcher/DaemonClientAction.java
deleted file mode 100644
index 219f574..0000000
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/DaemonClientAction.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.launcher;
-
-import org.gradle.api.Action;
-import org.gradle.launcher.protocol.Command;
-import org.gradle.launcher.protocol.CommandComplete;
-import org.gradle.logging.internal.OutputEvent;
-import org.gradle.logging.internal.OutputEventListener;
-import org.gradle.messaging.remote.internal.Connection;
-
-/**
- * The client piece of the build daemon.
- *
- * <p>Protocol is this:</p>
- *
- * <ol> <li>Client connects to the server.</li>
- *
- * <li>Client sends a {@link org.gradle.launcher.protocol.Command} message.</li>
- *
- * <li>Server sends zero or more {@link org.gradle.logging.internal.OutputEvent} messages. Note that the server may
- * send output messages before it receives the command message.
- * </li>
- *
- * <li>Server sends a {@link org.gradle.launcher.protocol.CommandComplete} message.</li>
- *
- * <li>Connection is closed.</li>
- *
- * </ol>
- */
-public abstract class DaemonClientAction implements Action<ExecutionListener> {
-    protected final OutputEventListener outputEventListener;
-
-    public DaemonClientAction(OutputEventListener outputEventListener) {
-        this.outputEventListener = outputEventListener;
-    }
-
-    protected void run(Command command, Connection<Object> connection, ExecutionListener executionListener) {
-        try {
-            connection.dispatch(command);
-
-            while (true) {
-                Object object = connection.receive();
-                if (object instanceof CommandComplete) {
-                    CommandComplete commandComplete = (CommandComplete) object;
-                    if (commandComplete.getFailure() != null) {
-                        executionListener.onFailure(commandComplete.getFailure());
-                    }
-                    break;
-                }
-                OutputEvent outputEvent = (OutputEvent) object;
-                outputEventListener.onOutput(outputEvent);
-            }
-        } finally {
-            connection.stop();
-        }
-    }
-}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/DaemonConnector.java b/subprojects/launcher/src/main/java/org/gradle/launcher/DaemonConnector.java
index bc36c87..eb16359 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/DaemonConnector.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/DaemonConnector.java
@@ -56,7 +56,7 @@ public class DaemonConnector {
      *
      * @return The connection, or null if not running.
      */
-    Connection<Object> maybeConnect() {
+    public Connection<Object> maybeConnect() {
         URI uri = loadDaemonAddress();
         if (uri == null) {
             return null;
@@ -98,7 +98,7 @@ public class DaemonConnector {
      *
      * @return The connection. Never returns null.
      */
-    Connection<Object> connect() {
+    public Connection<Object> connect() {
         Connection<Object> connection = maybeConnect();
         if (connection != null) {
             return connection;
@@ -202,7 +202,7 @@ public class DaemonConnector {
     }
 
     private File getRegistryFile() {
-        return new File(userHomeDir, String.format("daemon/%s/registry.bin", new GradleVersion().getVersion()));
+        return new File(userHomeDir, String.format("daemon/%s/registry.bin", GradleVersion.current().getVersion()));
     }
 
     private static class CompletionHandler implements Stoppable {
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/DaemonMain.java b/subprojects/launcher/src/main/java/org/gradle/launcher/DaemonMain.java
index c1b0a23..cb8b034 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/DaemonMain.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/DaemonMain.java
@@ -16,25 +16,20 @@
 package org.gradle.launcher;
 
 import org.gradle.BuildExceptionReporter;
-import org.gradle.api.logging.StandardOutputListener;
-import org.gradle.initialization.DefaultBuildRequestMetaData;
-import org.gradle.initialization.GradleLauncherFactory;
 import org.gradle.StartParameter;
 import org.gradle.api.internal.project.ServiceRegistry;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
 import org.gradle.initialization.DefaultCommandLineConverter;
 import org.gradle.initialization.DefaultGradleLauncherFactory;
-import org.gradle.launcher.protocol.Build;
-import org.gradle.launcher.protocol.Command;
-import org.gradle.launcher.protocol.CommandComplete;
-import org.gradle.launcher.protocol.Stop;
-import org.gradle.logging.LoggingManagerInternal;
+import org.gradle.initialization.GradleLauncherFactory;
+import org.gradle.launcher.protocol.*;
 import org.gradle.logging.LoggingServiceRegistry;
 import org.gradle.logging.StyledTextOutputFactory;
 import org.gradle.logging.internal.LoggingOutputInternal;
 import org.gradle.logging.internal.OutputEvent;
 import org.gradle.logging.internal.OutputEventListener;
+import org.gradle.logging.internal.StreamBackedStandardOutputListener;
 import org.gradle.messaging.concurrent.Stoppable;
 import org.gradle.messaging.remote.internal.Connection;
 import org.gradle.util.GradleVersion;
@@ -45,8 +40,7 @@ import java.util.Arrays;
 import java.util.Properties;
 
 /**
- * The server portion of the build daemon. See {@link DaemonClientAction} for a description of the
- * protocol.
+ * The server portion of the build daemon. See {@link DaemonClient} for a description of the protocol.
  */
 public class DaemonMain implements Runnable {
     private static final Logger LOGGER = Logging.getLogger(Main.class);
@@ -63,25 +57,16 @@ public class DaemonMain implements Runnable {
     public static void main(String[] args) throws IOException {
         StartParameter startParameter = new DefaultCommandLineConverter().convert(Arrays.asList(args));
         DaemonConnector connector = new DaemonConnector(startParameter.getGradleUserHomeDir());
-        LoggingServiceRegistry loggingServices = new LoggingServiceRegistry();
+        LoggingServiceRegistry loggingServices = LoggingServiceRegistry.newCommandLineProcessLogging();
         addLogFileWriters(startParameter, loggingServices);
         new DaemonMain(loggingServices, connector).run();
     }
 
     private static void addLogFileWriters(StartParameter startParameter, LoggingServiceRegistry loggingServices) throws IOException {
-        File stderrOut = new File(startParameter.getGradleUserHomeDir(), String.format("daemon/%s/daemon.err.log", new GradleVersion().getVersion()));
+        File stderrOut = new File(startParameter.getGradleUserHomeDir(), String.format("daemon/%s/daemon.err.log", GradleVersion.current().getVersion()));
         stderrOut.getParentFile().mkdirs();
         final Writer writer = new BufferedWriter(new FileWriter(stderrOut));
-        loggingServices.get(LoggingOutputInternal.class).addStandardErrorListener(new StandardOutputListener() {
-            public void onOutput(CharSequence output) {
-                try {
-                    writer.append(output);
-                    writer.flush();
-                } catch (IOException e) {
-                    throw UncheckedException.asUncheckedException(e);
-                }
-            }
-        });
+        loggingServices.get(LoggingOutputInternal.class).addStandardErrorListener(new StreamBackedStandardOutputListener(writer));
     }
 
     public void run() {
@@ -93,7 +78,8 @@ public class DaemonMain implements Runnable {
     }
 
     private void doRun(final Connection<Object> connection, Stoppable serverControl) {
-        ExecutionListenerImpl executionListener = new ExecutionListenerImpl();
+        CommandComplete result = null;
+        Throwable failure = null;
         try {
             LoggingOutputInternal loggingOutput = loggingServices.get(LoggingOutputInternal.class);
             OutputEventListener listener = new OutputEventListener() {
@@ -105,76 +91,59 @@ public class DaemonMain implements Runnable {
             // Perform as much as possible of the interaction while the logging is routed to the client
             loggingOutput.addOutputEventListener(listener);
             try {
-                doRunWithLogging(connection, serverControl, executionListener);
+                result = doRunWithLogging(connection, serverControl);
             } finally {
                 loggingOutput.removeOutputEventListener(listener);
             }
+        } catch (ReportedException e) {
+            failure = e;
         } catch (Throwable throwable) {
             LOGGER.error("Could not execute build.", throwable);
-            executionListener.onFailure(throwable);
+            failure = throwable;
         }
-        connection.dispatch(new CommandComplete(executionListener.failure));
+        if (failure != null) {
+            result = new CommandComplete(UncheckedException.asUncheckedException(failure));
+        }
+        assert result != null;
+        connection.dispatch(result);
     }
 
-    private void doRunWithLogging(Connection<Object> connection, Stoppable serverControl, ExecutionListener executionListener) {
+    private CommandComplete doRunWithLogging(Connection<Object> connection, Stoppable serverControl) {
         Command command = (Command) connection.receive();
         try {
-            doRunWithExceptionHandling(command, serverControl, executionListener);
+            return doRunWithExceptionHandling(command, serverControl);
+        } catch (ReportedException e) {
+            throw e;
         } catch (Throwable throwable) {
             BuildExceptionReporter exceptionReporter = new BuildExceptionReporter(loggingServices.get(StyledTextOutputFactory.class), new StartParameter(), command.getClientMetaData());
             exceptionReporter.reportException(throwable);
-            executionListener.onFailure(throwable);
+            throw new ReportedException(throwable);
         }
     }
 
-    private void doRunWithExceptionHandling(Command command, Stoppable serverControl, ExecutionListener executionListener) {
+    private CommandComplete doRunWithExceptionHandling(Command command, Stoppable serverControl) {
         LOGGER.info("Executing {}", command);
         if (command instanceof Stop) {
             LOGGER.lifecycle("Stopping");
             serverControl.stop();
-            return;
+            return new CommandComplete(null);
         }
 
-        assert command instanceof Build;
-        build((Build) command, executionListener);
+        return build((Build) command);
     }
 
-    private void build(Build build, ExecutionListener executionListener) {
-        DefaultCommandLineConverter converter = new DefaultCommandLineConverter();
-        StartParameter startParameter = new StartParameter();
-        startParameter.setCurrentDir(build.getCurrentDir());
-        converter.convert(build.getArgs(), startParameter);
-        LoggingManagerInternal loggingManager = loggingServices.getFactory(LoggingManagerInternal.class).create();
-        loggingManager.setLevel(startParameter.getLogLevel());
-        loggingManager.start();
-
-        Properties sysProperties = new Properties();
-        sysProperties.putAll(System.getProperties());
-
+    private Result build(Build build) {
+        Properties originalSystemProperties = new Properties();
+        originalSystemProperties.putAll(System.getProperties());
+        Properties clientSystemProperties = new Properties();
+        clientSystemProperties.putAll(build.getParameters().getSystemProperties());
+        System.setProperties(clientSystemProperties);
         try {
-            RunBuildAction action = new RunBuildAction(startParameter, loggingServices, new DefaultBuildRequestMetaData(build.getClientMetaData(), build.getStartTime())) {
-                @Override
-                GradleLauncherFactory createGradleLauncherFactory(ServiceRegistry loggingServices) {
-                    return launcherFactory;
-                }
-            };
-            action.execute(executionListener);
-        } catch (Throwable throwable) {
-            BuildExceptionReporter exceptionReporter = new BuildExceptionReporter(loggingServices.get(StyledTextOutputFactory.class), new StartParameter(), build.getClientMetaData());
-            exceptionReporter.reportException(throwable);
-            executionListener.onFailure(throwable);
-        }
-
-        loggingManager.stop();
-
-        System.setProperties(sysProperties);
-    }
-
-    private static class ExecutionListenerImpl implements ExecutionListener {
-        public Throwable failure;
-
-        public void onFailure(Throwable failure) {
-            this.failure = failure;
+            DefaultGradleLauncherActionExecuter executer = new DefaultGradleLauncherActionExecuter(launcherFactory, loggingServices);
+            Object result = executer.execute(build.getAction(), build.getParameters());
+            return new Result(result);
+        } finally {
+            System.setProperties(originalSystemProperties);
         }
     }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/DefaultBuildActionParameters.java b/subprojects/launcher/src/main/java/org/gradle/launcher/DefaultBuildActionParameters.java
new file mode 100644
index 0000000..aa1ee35
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/DefaultBuildActionParameters.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.BuildClientMetaData;
+import org.gradle.initialization.BuildRequestMetaData;
+import org.gradle.initialization.DefaultBuildRequestMetaData;
+import org.gradle.util.GUtil;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+public class DefaultBuildActionParameters implements BuildActionParameters, Serializable {
+    private final BuildClientMetaData clientMetaData;
+    private final long startTime;
+    private final Map<String, String> systemProperties;
+
+    public DefaultBuildActionParameters(BuildClientMetaData clientMetaData, long startTime, Map<?, ?> systemProperties) {
+        this.clientMetaData = clientMetaData;
+        this.startTime = startTime;
+        this.systemProperties = new HashMap<String, String>();
+        GUtil.addToMap(this.systemProperties, systemProperties);
+    }
+
+    public BuildRequestMetaData getBuildRequestMetaData() {
+        return new DefaultBuildRequestMetaData(clientMetaData, startTime);
+    }
+
+    public BuildClientMetaData getClientMetaData() {
+        return clientMetaData;
+    }
+
+    public Map<String, String> getSystemProperties() {
+        return systemProperties;
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/DefaultGradleLauncherActionExecuter.java b/subprojects/launcher/src/main/java/org/gradle/launcher/DefaultGradleLauncherActionExecuter.java
new file mode 100644
index 0000000..41a13ad
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/DefaultGradleLauncherActionExecuter.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.BuildExceptionReporter;
+import org.gradle.BuildResult;
+import org.gradle.GradleLauncher;
+import org.gradle.StartParameter;
+import org.gradle.api.internal.project.ServiceRegistry;
+import org.gradle.initialization.GradleLauncherAction;
+import org.gradle.initialization.GradleLauncherFactory;
+import org.gradle.logging.LoggingManagerInternal;
+import org.gradle.logging.StyledTextOutputFactory;
+
+public class DefaultGradleLauncherActionExecuter implements GradleLauncherActionExecuter<BuildActionParameters> {
+    private final ServiceRegistry loggingServices;
+    private final GradleLauncherFactory gradleLauncherFactory;
+
+    public DefaultGradleLauncherActionExecuter(GradleLauncherFactory gradleLauncherFactory, ServiceRegistry loggingServices) {
+        this.gradleLauncherFactory = gradleLauncherFactory;
+        this.loggingServices = loggingServices;
+    }
+
+    public <T> T execute(GradleLauncherAction<T> action, BuildActionParameters parameters) {
+        StartParameter startParameter = new StartParameter();
+        if (action instanceof InitializationAware) {
+            InitializationAware initializationAware = (InitializationAware) action;
+            initializationAware.configureStartParameter(startParameter);
+        }
+
+        LoggingManagerInternal loggingManager = loggingServices.getFactory(LoggingManagerInternal.class).create();
+        loggingManager.setLevel(startParameter.getLogLevel());
+        loggingManager.start();
+        try {
+            GradleLauncher gradleLauncher = gradleLauncherFactory.newInstance(startParameter, parameters.getBuildRequestMetaData());
+            BuildResult buildResult = action.run(gradleLauncher);
+            Throwable failure = buildResult.getFailure();
+            if (failure != null) {
+                throw new ReportedException(failure);
+            }
+            return action.getResult();
+        } catch (ReportedException e) {
+            throw e;
+        } catch (Throwable throwable) {
+            BuildExceptionReporter exceptionReporter = new BuildExceptionReporter(loggingServices.get(StyledTextOutputFactory.class), new StartParameter(), parameters.getClientMetaData());
+            exceptionReporter.reportException(throwable);
+            throw new ReportedException(throwable);
+        } finally {
+            loggingManager.stop();
+        }
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/ExceptionReportingAction.java b/subprojects/launcher/src/main/java/org/gradle/launcher/ExceptionReportingAction.java
new file mode 100644
index 0000000..3754161
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/ExceptionReportingAction.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.BuildExceptionReporter;
+import org.gradle.api.Action;
+
+public class ExceptionReportingAction implements Action<ExecutionListener> {
+    private final Action<ExecutionListener> action;
+    private final BuildExceptionReporter reporter;
+
+    public ExceptionReportingAction(Action<ExecutionListener> action, BuildExceptionReporter reporter) {
+        this.action = action;
+        this.reporter = reporter;
+    }
+
+    public void execute(ExecutionListener executionListener) {
+        try {
+            action.execute(executionListener);
+        } catch (ReportedException e) {
+            executionListener.onFailure(e.getCause());
+        } catch (Throwable t) {
+            reporter.reportException(t);
+            executionListener.onFailure(t);
+        }
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/ExecuteBuildAction.java b/subprojects/launcher/src/main/java/org/gradle/launcher/ExecuteBuildAction.java
new file mode 100644
index 0000000..b272292
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/ExecuteBuildAction.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.BuildResult;
+import org.gradle.GradleLauncher;
+import org.gradle.StartParameter;
+import org.gradle.initialization.DefaultCommandLineConverter;
+import org.gradle.initialization.GradleLauncherAction;
+import org.gradle.initialization.ParsedCommandLine;
+
+import java.io.File;
+import java.io.Serializable;
+
+public class ExecuteBuildAction implements GradleLauncherAction<Void>, InitializationAware, Serializable {
+    private final File currentDir;
+    private final ParsedCommandLine args;
+
+    public ExecuteBuildAction(File currentDir, ParsedCommandLine args) {
+        this.currentDir = currentDir;
+        this.args = args;
+    }
+
+    public void configureStartParameter(StartParameter startParameter) {
+        DefaultCommandLineConverter converter = new DefaultCommandLineConverter();
+        startParameter.setCurrentDir(currentDir);
+        converter.convert(args, startParameter);
+    }
+
+    public BuildResult run(GradleLauncher launcher) {
+        return launcher.run();
+    }
+
+    public Void getResult() {
+        return null;
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/GradleLauncherActionExecuter.java b/subprojects/launcher/src/main/java/org/gradle/launcher/GradleLauncherActionExecuter.java
new file mode 100644
index 0000000..14026e2
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/GradleLauncherActionExecuter.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.GradleLauncherAction;
+
+public interface GradleLauncherActionExecuter<P> {
+    /**
+     * Executes the given action, and returns the result. The given action may also implement {@link InitializationAware <T>}.
+     *
+     * @param action The action
+     * @param <T> The result type
+     * @return The result.
+     */
+    <T> T execute(GradleLauncherAction<T> action, P actionParameters);
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/InitializationAware.java b/subprojects/launcher/src/main/java/org/gradle/launcher/InitializationAware.java
new file mode 100644
index 0000000..0c81795
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/InitializationAware.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.StartParameter;
+
+public interface InitializationAware {
+    void configureStartParameter(StartParameter startParameter);
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/ReportedException.java b/subprojects/launcher/src/main/java/org/gradle/launcher/ReportedException.java
new file mode 100644
index 0000000..9060079
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/ReportedException.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+/**
+ * Wraps an exception which has already been logged, and should not be logged again.
+ */
+public class ReportedException extends RuntimeException {
+    public ReportedException(Throwable throwable) {
+        super(throwable);
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/StopDaemonAction.java b/subprojects/launcher/src/main/java/org/gradle/launcher/StopDaemonAction.java
index f2518d3..ab7d788 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/StopDaemonAction.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/StopDaemonAction.java
@@ -15,32 +15,14 @@
  */
 package org.gradle.launcher;
 
-import org.gradle.api.Action;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-import org.gradle.initialization.BuildClientMetaData;
-import org.gradle.launcher.protocol.Stop;
-import org.gradle.logging.internal.OutputEventListener;
-import org.gradle.messaging.remote.internal.Connection;
+public class StopDaemonAction implements Runnable {
+    private final DaemonClient client;
 
-public class StopDaemonAction extends DaemonClientAction implements Action<ExecutionListener> {
-    private static final Logger LOGGER = Logging.getLogger(StopDaemonAction.class);
-    private final DaemonConnector connector;
-    private final BuildClientMetaData clientMetaData;
-
-    public StopDaemonAction(DaemonConnector connector, OutputEventListener outputEventListener, BuildClientMetaData clientMetaData) {
-        super(outputEventListener);
-        this.connector = connector;
-        this.clientMetaData = clientMetaData;
+    public StopDaemonAction(DaemonClient client) {
+        this.client = client;
     }
 
-    public void execute(ExecutionListener executionListener) {
-        Connection<Object> connection = connector.maybeConnect();
-        if (connection == null) {
-            LOGGER.lifecycle("Gradle daemon is not running.");
-            return;
-        }
-        run(new Stop(clientMetaData), connection, executionListener);
-        LOGGER.lifecycle("Gradle daemon stopped.");
+    public void run() {
+        client.stop();
     }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/protocol/Build.java b/subprojects/launcher/src/main/java/org/gradle/launcher/protocol/Build.java
index b79e457..7a99eb7 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/protocol/Build.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/protocol/Build.java
@@ -15,32 +15,24 @@
  */
 package org.gradle.launcher.protocol;
 
-import org.gradle.initialization.BuildClientMetaData;
-import org.gradle.initialization.ParsedCommandLine;
-
-import java.io.File;
+import org.gradle.initialization.GradleLauncherAction;
+import org.gradle.launcher.BuildActionParameters;
 
 public class Build extends Command {
-    private final ParsedCommandLine args;
-    private final File currentDir;
-    private final long startTime;
-
-    public Build(File currentDir, ParsedCommandLine args, long startTime, BuildClientMetaData clientMetaData) {
-        super(clientMetaData);
-        this.currentDir = currentDir;
-        this.args = args;
-        this.startTime = startTime;
-    }
+    private final GradleLauncherAction<?> action;
+    private final BuildActionParameters parameters;
 
-    public ParsedCommandLine getArgs() {
-        return args;
+    public Build(GradleLauncherAction<?> action, BuildActionParameters parameters) {
+        super(parameters.getClientMetaData());
+        this.action = action;
+        this.parameters = parameters;
     }
 
-    public File getCurrentDir() {
-        return currentDir;
+    public GradleLauncherAction<?> getAction() {
+        return action;
     }
 
-    public long getStartTime() {
-        return startTime;
+    public BuildActionParameters getParameters() {
+        return parameters;
     }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/protocol/CommandComplete.java b/subprojects/launcher/src/main/java/org/gradle/launcher/protocol/CommandComplete.java
index 96698a6..a9ccdab 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/protocol/CommandComplete.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/protocol/CommandComplete.java
@@ -18,13 +18,13 @@ package org.gradle.launcher.protocol;
 import java.io.Serializable;
 
 public class CommandComplete implements Serializable {
-    private final Throwable failure;
+    private final RuntimeException failure;
 
-    public CommandComplete(Throwable failure) {
+    public CommandComplete(RuntimeException failure) {
         this.failure = failure;
     }
 
-    public Throwable getFailure() {
+    public RuntimeException getFailure() {
         return failure;
     }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/protocol/Result.java b/subprojects/launcher/src/main/java/org/gradle/launcher/protocol/Result.java
new file mode 100644
index 0000000..daf9335
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/protocol/Result.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.protocol;
+
+public class Result extends CommandComplete {
+    private final Object result;
+
+    public Result(Object result) {
+        super(null);
+        this.result = result;
+    }
+
+    public Object getResult() {
+        return result;
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ConfiguringBuildAction.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ConfiguringBuildAction.java
new file mode 100644
index 0000000..a922281
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ConfiguringBuildAction.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.provider;
+
+import org.gradle.BuildResult;
+import org.gradle.GradleLauncher;
+import org.gradle.StartParameter;
+import org.gradle.initialization.GradleLauncherAction;
+import org.gradle.launcher.InitializationAware;
+
+import java.io.File;
+import java.io.Serializable;
+
+class ConfiguringBuildAction<T> implements GradleLauncherAction<T>, InitializationAware, Serializable {
+    private final GradleLauncherAction<T> action;
+    private final File projectDirectory;
+    private final File gradleUserHomeDir;
+    private final Boolean searchUpwards;
+
+    ConfiguringBuildAction(File gradleUserHomeDir, File projectDirectory, Boolean searchUpwards, GradleLauncherAction<T> action) {
+        this.gradleUserHomeDir = gradleUserHomeDir;
+        this.projectDirectory = projectDirectory;
+        this.searchUpwards = searchUpwards;
+        this.action = action;
+    }
+
+    public void configureStartParameter(StartParameter startParameter) {
+        startParameter.setProjectDir(projectDirectory);
+        if (gradleUserHomeDir != null) {
+            startParameter.setGradleUserHomeDir(gradleUserHomeDir);
+        }
+        if (searchUpwards != null) {
+            startParameter.setSearchUpwards(searchUpwards);
+        }
+        startParameter.setShowStacktrace(StartParameter.ShowStacktrace.ALWAYS);
+        if (action instanceof InitializationAware) {
+            InitializationAware initializationAware = (InitializationAware) action;
+            initializationAware.configureStartParameter(startParameter);
+        }
+    }
+
+    public BuildResult run(GradleLauncher launcher) {
+        return action.run(launcher);
+    }
+
+    public T getResult() {
+        return action.getResult();
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DaemonGradleLauncherActionExecuter.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DaemonGradleLauncherActionExecuter.java
new file mode 100644
index 0000000..911d52c
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DaemonGradleLauncherActionExecuter.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.provider;
+
+import org.gradle.configuration.GradleLauncherMetaData;
+import org.gradle.initialization.GradleLauncherAction;
+import org.gradle.launcher.*;
+import org.gradle.tooling.internal.protocol.BuildExceptionVersion1;
+import org.gradle.tooling.internal.protocol.BuildOperationParametersVersion1;
+
+public class DaemonGradleLauncherActionExecuter implements GradleLauncherActionExecuter<BuildOperationParametersVersion1> {
+    private final DaemonClient client;
+
+    public DaemonGradleLauncherActionExecuter(DaemonClient client) {
+        this.client = client;
+    }
+
+    public <T> T execute(GradleLauncherAction<T> action, BuildOperationParametersVersion1 actionParameters) {
+        BuildActionParameters parameters = new DefaultBuildActionParameters(new GradleLauncherMetaData(), actionParameters.getStartTime(), System.getProperties());
+        try {
+            return client.execute(action, parameters);
+        } catch (ReportedException e) {
+            throw new BuildExceptionVersion1(e.getCause());
+        }
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DefaultConnection.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DefaultConnection.java
new file mode 100644
index 0000000..4defcd8
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DefaultConnection.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.provider;
+
+import org.gradle.GradleLauncher;
+import org.gradle.StartParameter;
+import org.gradle.api.internal.project.ServiceRegistry;
+import org.gradle.configuration.GradleLauncherMetaData;
+import org.gradle.initialization.DefaultGradleLauncherFactory;
+import org.gradle.initialization.GradleLauncherAction;
+import org.gradle.initialization.GradleLauncherFactory;
+import org.gradle.launcher.DaemonClient;
+import org.gradle.launcher.DaemonConnector;
+import org.gradle.launcher.GradleLauncherActionExecuter;
+import org.gradle.logging.LoggingManagerInternal;
+import org.gradle.logging.LoggingServiceRegistry;
+import org.gradle.logging.internal.OutputEventListener;
+import org.gradle.tooling.internal.protocol.*;
+import org.gradle.util.GUtil;
+import org.gradle.util.GradleVersion;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+
+public class DefaultConnection implements ConnectionVersion4 {
+    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultConnection.class);
+    private final ServiceRegistry loggingServices;
+    private final GradleLauncherFactory gradleLauncherFactory;
+
+    public DefaultConnection() {
+        LOGGER.debug("Using tooling API provider version {}.", GradleVersion.current().getVersion());
+        loggingServices = LoggingServiceRegistry.newEmbeddableLogging();
+        gradleLauncherFactory = new DefaultGradleLauncherFactory(loggingServices);
+        GradleLauncher.injectCustomFactory(gradleLauncherFactory);
+    }
+
+    public ConnectionMetaDataVersion1 getMetaData() {
+        return new ConnectionMetaDataVersion1() {
+            public String getVersion() {
+                return GradleVersion.current().getVersion();
+            }
+
+            public String getDisplayName() {
+                return String.format("Gradle %s", getVersion());
+            }
+        };
+    }
+
+    public void stop() {
+    }
+
+    public void executeBuild(final BuildParametersVersion1 buildParameters, BuildOperationParametersVersion1 operationParameters) {
+        run(new ExecuteBuildAction(buildParameters.getTasks()), operationParameters);
+    }
+
+    public ProjectVersion3 getModel(Class<? extends ProjectVersion3> type, BuildOperationParametersVersion1 operationParameters) {
+        GradleLauncherAction<ProjectVersion3> action = new DelegatingBuildModelAction(type);
+        return run(action, operationParameters);
+    }
+
+    private <T> T run(GradleLauncherAction<T> action, BuildOperationParametersVersion1 operationParameters) {
+        GradleLauncherActionExecuter<BuildOperationParametersVersion1> executer = createExecuter(operationParameters);
+        ConfiguringBuildAction<T> configuringAction = new ConfiguringBuildAction<T>(operationParameters.getGradleUserHomeDir(), operationParameters.getProjectDir(), operationParameters.isSearchUpwards(), action);
+        return executer.execute(configuringAction, operationParameters);
+    }
+
+    private GradleLauncherActionExecuter<BuildOperationParametersVersion1> createExecuter(BuildOperationParametersVersion1 operationParameters) {
+        GradleLauncherActionExecuter<BuildOperationParametersVersion1> executer;
+        if (Boolean.TRUE.equals(operationParameters.isEmbedded())) {
+            executer = new EmbeddedGradleLauncherActionExecuter(gradleLauncherFactory);
+        } else {
+            File gradleUserHomeDir = GUtil.elvis(operationParameters.getGradleUserHomeDir(), StartParameter.DEFAULT_GRADLE_USER_HOME);
+            DaemonClient client = new DaemonClient(new DaemonConnector(gradleUserHomeDir), new GradleLauncherMetaData(), loggingServices.get(OutputEventListener.class));
+            executer = new DaemonGradleLauncherActionExecuter(client);
+        }
+        return new LoggingBridgingGradleLauncherActionExecuter(executer, loggingServices.getFactory(LoggingManagerInternal.class));
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DelegatingBuildModelAction.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DelegatingBuildModelAction.java
new file mode 100644
index 0000000..88e1605
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DelegatingBuildModelAction.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.provider;
+
+import org.gradle.BuildResult;
+import org.gradle.GradleLauncher;
+import org.gradle.initialization.ClassLoaderFactory;
+import org.gradle.initialization.DefaultGradleLauncher;
+import org.gradle.initialization.GradleLauncherAction;
+import org.gradle.tooling.internal.protocol.ProjectVersion3;
+import org.gradle.util.UncheckedException;
+
+import java.io.Serializable;
+
+class DelegatingBuildModelAction implements GradleLauncherAction<ProjectVersion3>, Serializable {
+    private transient GradleLauncherAction<ProjectVersion3> action;
+    private final Class<? extends ProjectVersion3> type;
+
+    public DelegatingBuildModelAction(Class<? extends ProjectVersion3> type) {
+        this.type = type;
+    }
+
+    public ProjectVersion3 getResult() {
+        return action.getResult();
+    }
+
+    public BuildResult run(GradleLauncher launcher) {
+        loadAction((DefaultGradleLauncher) launcher);
+        return action.run(launcher);
+    }
+
+    private void loadAction(DefaultGradleLauncher launcher) {
+        DefaultGradleLauncher gradleLauncher = launcher;
+        ClassLoaderFactory classLoaderFactory = gradleLauncher.getGradle().getServices().get(ClassLoaderFactory.class);
+        try {
+            action = (GradleLauncherAction<ProjectVersion3>) classLoaderFactory.getRootClassLoader().loadClass("org.gradle.tooling.internal.provider.BuildModelAction").getConstructor(Class.class).newInstance(type);
+        } catch (Exception e) {
+            throw UncheckedException.asUncheckedException(e);
+        }
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/EmbeddedGradleLauncherActionExecuter.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/EmbeddedGradleLauncherActionExecuter.java
new file mode 100644
index 0000000..f73945a
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/EmbeddedGradleLauncherActionExecuter.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.provider;
+
+import org.gradle.BuildResult;
+import org.gradle.GradleLauncher;
+import org.gradle.StartParameter;
+import org.gradle.initialization.GradleLauncherAction;
+import org.gradle.initialization.GradleLauncherFactory;
+import org.gradle.launcher.GradleLauncherActionExecuter;
+import org.gradle.launcher.InitializationAware;
+import org.gradle.tooling.internal.protocol.BuildExceptionVersion1;
+import org.gradle.tooling.internal.protocol.BuildOperationParametersVersion1;
+
+/**
+ * A {@link GradleLauncherActionExecuter} which executes an action locally.
+ */
+public class EmbeddedGradleLauncherActionExecuter implements GradleLauncherActionExecuter<BuildOperationParametersVersion1> {
+    private final GradleLauncherFactory gradleLauncherFactory;
+
+    public EmbeddedGradleLauncherActionExecuter(GradleLauncherFactory gradleLauncherFactory) {
+        this.gradleLauncherFactory = gradleLauncherFactory;
+    }
+
+    public <T> T execute(GradleLauncherAction<T> action, BuildOperationParametersVersion1 actionParameters) {
+        StartParameter startParameter = new StartParameter();
+        if (action instanceof InitializationAware) {
+            InitializationAware initializationAware = (InitializationAware) action;
+            initializationAware.configureStartParameter(startParameter);
+        }
+        GradleLauncher gradleLauncher = gradleLauncherFactory.newInstance(startParameter);
+        BuildResult result = action.run(gradleLauncher);
+        if (result.getFailure() != null) {
+            throw new BuildExceptionVersion1(result.getFailure());
+        }
+        return action.getResult();
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ExecuteBuildAction.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ExecuteBuildAction.java
new file mode 100644
index 0000000..a568cef
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ExecuteBuildAction.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.provider;
+
+import org.gradle.BuildResult;
+import org.gradle.GradleLauncher;
+import org.gradle.StartParameter;
+import org.gradle.initialization.GradleLauncherAction;
+import org.gradle.launcher.InitializationAware;
+
+import java.io.Serializable;
+import java.util.List;
+
+class ExecuteBuildAction implements GradleLauncherAction<Void>, InitializationAware, Serializable {
+    private final List<String> tasks;
+
+    public ExecuteBuildAction(List<String> tasks) {
+        this.tasks = tasks;
+    }
+
+    public Void getResult() {
+        return null;
+    }
+
+    public void configureStartParameter(StartParameter startParameter) {
+        startParameter.setTaskNames(tasks);
+    }
+
+    public BuildResult run(GradleLauncher gradleLauncher) {
+        return gradleLauncher.run();
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/LoggingBridgingGradleLauncherActionExecuter.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/LoggingBridgingGradleLauncherActionExecuter.java
new file mode 100644
index 0000000..985bfdc
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/LoggingBridgingGradleLauncherActionExecuter.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.provider;
+
+import org.gradle.api.internal.Factory;
+import org.gradle.initialization.GradleLauncherAction;
+import org.gradle.launcher.GradleLauncherActionExecuter;
+import org.gradle.logging.LoggingManagerInternal;
+import org.gradle.logging.internal.*;
+import org.gradle.tooling.internal.protocol.BuildOperationParametersVersion1;
+import org.gradle.tooling.internal.protocol.ProgressListenerVersion1;
+
+/**
+ * A {@link org.gradle.launcher.GradleLauncherActionExecuter} which routes Gradle logging to those listeners specified in the {@link BuildOperationParametersVersion1} provided with a tooling api build
+ * request.
+ */
+public class LoggingBridgingGradleLauncherActionExecuter implements GradleLauncherActionExecuter<BuildOperationParametersVersion1> {
+    private final Factory<LoggingManagerInternal> loggingManagerFactory;
+    private final GradleLauncherActionExecuter<BuildOperationParametersVersion1> executer;
+
+    public LoggingBridgingGradleLauncherActionExecuter(GradleLauncherActionExecuter<BuildOperationParametersVersion1> executer, Factory<LoggingManagerInternal> loggingManagerFactory) {
+        this.executer = executer;
+        this.loggingManagerFactory = loggingManagerFactory;
+    }
+
+    public <T> T execute(GradleLauncherAction<T> action, BuildOperationParametersVersion1 actionParameters) {
+        LoggingManagerInternal loggingManager = loggingManagerFactory.create();
+        if (!Boolean.TRUE.equals(actionParameters.isEmbedded())) {
+            loggingManager.disableStandardOutputCapture();
+        }
+        if (actionParameters.getStandardOutput() != null) {
+            loggingManager.addStandardOutputListener(new StreamBackedStandardOutputListener(actionParameters.getStandardOutput()));
+        }
+        if (actionParameters.getStandardError() != null) {
+            loggingManager.addStandardErrorListener(new StreamBackedStandardOutputListener(actionParameters.getStandardError()));
+        }
+        ProgressListenerVersion1 progressListener = actionParameters.getProgressListener();
+        OutputEventListenerAdapter listener = new OutputEventListenerAdapter(progressListener);
+        loggingManager.addOutputEventListener(listener);
+
+        loggingManager.start();
+        try {
+            return executer.execute(action, actionParameters);
+        } finally {
+            loggingManager.stop();
+        }
+    }
+
+    private static class OutputEventListenerAdapter implements OutputEventListener {
+        private final ProgressListenerVersion1 progressListener;
+
+        public OutputEventListenerAdapter(ProgressListenerVersion1 progressListener) {
+            this.progressListener = progressListener;
+        }
+
+        public void onOutput(OutputEvent event) {
+            if (event instanceof ProgressStartEvent) {
+                ProgressStartEvent startEvent = (ProgressStartEvent) event;
+                progressListener.onOperationStart(startEvent.getDescription());
+            } else if (event instanceof ProgressCompleteEvent) {
+                progressListener.onOperationEnd();
+            }
+        }
+    }
+}
diff --git a/subprojects/launcher/src/main/resources/META-INF/services/org.gradle.tooling.internal.protocol.ConnectionVersion4 b/subprojects/launcher/src/main/resources/META-INF/services/org.gradle.tooling.internal.protocol.ConnectionVersion4
new file mode 100644
index 0000000..2eb9ec3
--- /dev/null
+++ b/subprojects/launcher/src/main/resources/META-INF/services/org.gradle.tooling.internal.protocol.ConnectionVersion4
@@ -0,0 +1 @@
+org.gradle.tooling.internal.provider.DefaultConnection
\ No newline at end of file
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/CommandLineActionFactoryTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/CommandLineActionFactoryTest.groovy
index 342d4a7..7b392e7 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/CommandLineActionFactoryTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/CommandLineActionFactoryTest.groovy
@@ -120,7 +120,7 @@ class CommandLineActionFactoryTest extends Specification {
 
         then:
         1 * loggingManager.start()
-        outputs.stdOut.contains(new GradleVersion().prettyPrint())
+        outputs.stdOut.contains(GradleVersion.current().prettyPrint())
 
         where:
         option << ['-v', '--version']
@@ -132,8 +132,9 @@ class CommandLineActionFactoryTest extends Specification {
 
         then:
         action instanceof WithLoggingAction
-        action.action instanceof ActionAdapter
-        action.action.action instanceof ShowGuiAction
+        action.action instanceof ExceptionReportingAction
+        action.action.action instanceof ActionAdapter
+        action.action.action.action instanceof ShowGuiAction
     }
 
     def executesBuild() {
@@ -142,7 +143,8 @@ class CommandLineActionFactoryTest extends Specification {
 
         then:
         action instanceof WithLoggingAction
-        action.action instanceof RunBuildAction
+        action.action instanceof ExceptionReportingAction
+        action.action.action instanceof RunBuildAction
     }
 
     def executesBuildUsingDaemon() {
@@ -151,7 +153,9 @@ class CommandLineActionFactoryTest extends Specification {
 
         then:
         action instanceof WithLoggingAction
-        action.action instanceof DaemonBuildAction
+        action.action instanceof ExceptionReportingAction
+        action.action.action instanceof ActionAdapter
+        action.action.action.action instanceof DaemonBuildAction
     }
 
     def executesBuildUsingDaemonWhenSystemPropertyIsSetToTrue() {
@@ -161,7 +165,7 @@ class CommandLineActionFactoryTest extends Specification {
 
         then:
         action instanceof WithLoggingAction
-        action.action instanceof RunBuildAction
+        action.action.action instanceof RunBuildAction
 
         when:
         System.properties['org.gradle.daemon'] = 'true'
@@ -169,7 +173,7 @@ class CommandLineActionFactoryTest extends Specification {
 
         then:
         action instanceof WithLoggingAction
-        action.action instanceof DaemonBuildAction
+        action.action.action.action instanceof DaemonBuildAction
     }
 
     def doesNotUseDaemonWhenNoDaemonOptionPresent() {
@@ -178,7 +182,7 @@ class CommandLineActionFactoryTest extends Specification {
 
         then:
         action instanceof WithLoggingAction
-        action.action instanceof RunBuildAction
+        action.action.action instanceof RunBuildAction
     }
 
     def daemonOptionTakesPrecedenceOverSystemProperty() {
@@ -188,7 +192,7 @@ class CommandLineActionFactoryTest extends Specification {
 
         then:
         action instanceof WithLoggingAction
-        action.action instanceof DaemonBuildAction
+        action.action.action.action instanceof DaemonBuildAction
 
         when:
         System.properties['org.gradle.daemon'] = 'true'
@@ -196,7 +200,7 @@ class CommandLineActionFactoryTest extends Specification {
 
         then:
         action instanceof WithLoggingAction
-        action.action instanceof RunBuildAction
+        action.action.action instanceof RunBuildAction
     }
     
     def stopsDaemon() {
@@ -205,7 +209,9 @@ class CommandLineActionFactoryTest extends Specification {
 
         then:
         action instanceof WithLoggingAction
-        action.action instanceof StopDaemonAction
+        action.action instanceof ExceptionReportingAction
+        action.action.action instanceof ActionAdapter
+        action.action.action.action instanceof StopDaemonAction
     }
 
     def runsDaemonInForeground() {
@@ -214,7 +220,8 @@ class CommandLineActionFactoryTest extends Specification {
 
         then:
         action instanceof WithLoggingAction
-        action.action instanceof ActionAdapter
-        action.action.action instanceof DaemonMain
+        action.action instanceof ExceptionReportingAction
+        action.action.action instanceof ActionAdapter
+        action.action.action.action instanceof DaemonMain
     }
 }
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/DaemonBuildActionTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/DaemonBuildActionTest.groovy
index 87481e3..68221c5 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/DaemonBuildActionTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/DaemonBuildActionTest.groovy
@@ -15,41 +15,33 @@
  */
 package org.gradle.launcher
 
+import org.gradle.initialization.BuildClientMetaData
 import org.gradle.initialization.ParsedCommandLine
-import org.gradle.launcher.protocol.Build
-import org.gradle.launcher.protocol.CommandComplete
-import org.gradle.logging.internal.OutputEventListener
-import org.gradle.messaging.remote.internal.Connection
 import spock.lang.Specification
-import org.gradle.initialization.BuildClientMetaData
 
 class DaemonBuildActionTest extends Specification {
-    final DaemonConnector connector = Mock()
-    final OutputEventListener listener = Mock()
-    final ExecutionListener completer = Mock()
+    final DaemonClient client = Mock()
     final ParsedCommandLine commandLine = Mock()
     final BuildClientMetaData clientMetaData = Mock()
     final File currentDir = new File('current-dir')
     final long startTime = 90
-    final DaemonBuildAction action = new DaemonBuildAction(listener, connector, commandLine, currentDir, clientMetaData, startTime)
+    final Map<String, String> systemProperties = [key: 'value']
+    final DaemonBuildAction action = new DaemonBuildAction(client, commandLine, currentDir, clientMetaData, startTime, systemProperties)
 
     def runsBuildUsingDaemon() {
-        Connection<Object> connection = Mock()
-
         when:
-        action.execute(completer)
+        action.run()
 
         then:
-        1 * connector.connect() >> connection
-        1 * connection.dispatch({!null}) >> { args ->
-            Build build = args[0]
-            assert build.currentDir == currentDir
-            assert build.args == commandLine
+        1 * client.execute({!null}, {!null}) >> { args ->
+            ExecuteBuildAction action = args[0]
+            assert action.currentDir == currentDir
+            assert action.args == commandLine
+            BuildActionParameters build = args[1]
             assert build.clientMetaData == clientMetaData
             assert build.startTime == startTime
+            assert build.systemProperties == systemProperties
         }
-        1 * connection.receive() >> new CommandComplete(null)
-        1 * connection.stop()
         0 * _._
     }
 }
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/DaemonClientTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/DaemonClientTest.groovy
new file mode 100644
index 0000000..2fba724
--- /dev/null
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/DaemonClientTest.groovy
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.BuildClientMetaData
+import org.gradle.initialization.GradleLauncherAction
+import org.gradle.launcher.protocol.Build
+import org.gradle.launcher.protocol.CommandComplete
+import org.gradle.launcher.protocol.Result
+import org.gradle.launcher.protocol.Stop
+import org.gradle.logging.internal.OutputEventListener
+import org.gradle.messaging.remote.internal.Connection
+import spock.lang.Specification
+
+class DaemonClientTest extends Specification {
+    final DaemonConnector connector = Mock()
+    final Connection<Object> connection = Mock()
+    final BuildClientMetaData metaData = Mock()
+    final OutputEventListener outputEventListener = Mock()
+    final DaemonClient client = new DaemonClient(connector, metaData, outputEventListener)
+
+    def stopsTheDaemonWhenRunning() {
+        when:
+        client.stop()
+
+        then:
+        1 * connector.maybeConnect() >> connection
+        1 * connection.dispatch({it instanceof Stop})
+        1 * connection.receive() >> new CommandComplete(null)
+        1 * connection.stop()
+        0 * _._
+    }
+
+    def stopsTheDaemonWhenNotRunning() {
+        when:
+        client.stop()
+
+        then:
+        1 * connector.maybeConnect() >> null
+        0 * _._
+    }
+
+    def rethrowsFailureToStopDaemon() {
+        RuntimeException failure = new RuntimeException()
+
+        when:
+        client.stop()
+
+        then:
+        RuntimeException e = thrown()
+        e == failure
+        1 * connector.maybeConnect() >> connection
+        1 * connection.dispatch({it instanceof Stop})
+        1 * connection.receive() >> new CommandComplete(failure)
+        1 * connection.stop()
+        0 * _._
+    }
+
+    def executesAction() {
+        GradleLauncherAction<String> action = Mock()
+        BuildActionParameters parameters = Mock()
+
+        when:
+        def result = client.execute(action, parameters)
+
+        then:
+        result == '[result]'
+        1 * connector.connect() >> connection
+        1 * connection.dispatch({it instanceof Build})
+        1 * connection.receive() >> new Result('[result]')
+        1 * connection.stop()
+    }
+
+    def rethrowsFailureToExecuteAction() {
+        GradleLauncherAction<String> action = Mock()
+        BuildActionParameters parameters = Mock()
+        RuntimeException failure = new RuntimeException()
+
+        when:
+        client.execute(action, parameters)
+
+        then:
+        RuntimeException e = thrown()
+        e == failure
+        1 * connector.connect() >> connection
+        1 * connection.dispatch({it instanceof Build})
+        1 * connection.receive() >> new CommandComplete(failure)
+        1 * connection.stop()
+    }
+}
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/ExceptionReportingActionTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/ExceptionReportingActionTest.groovy
new file mode 100644
index 0000000..e360bf2
--- /dev/null
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/ExceptionReportingActionTest.groovy
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.BuildExceptionReporter
+import org.gradle.api.Action
+import spock.lang.Specification
+
+class ExceptionReportingActionTest extends Specification {
+    final Action<ExecutionListener> target = Mock()
+    final ExecutionListener listener = Mock()
+    final BuildExceptionReporter reporter = Mock()
+    final ExceptionReportingAction action = new ExceptionReportingAction(target, reporter)
+
+    def executesAction() {
+        when:
+        action.execute(listener)
+
+        then:
+        1 * target.execute(listener)
+        0 * _._
+    }
+
+    def reportsExceptionThrownByAction() {
+        def failure = new RuntimeException()
+
+        when:
+        action.execute(listener)
+
+        then:
+        1 * target.execute(listener) >> { throw failure }
+        1 * reporter.reportException(failure)
+        1 * listener.onFailure(failure)
+        0 * _._
+    }
+
+    def doesNotReportAlreadyReportedExceptionThrownByAction() {
+        def cause = new RuntimeException()
+        def failure = new ReportedException(cause)
+
+        when:
+        action.execute(listener)
+
+        then:
+        1 * target.execute(listener) >> { throw failure }
+        1 * listener.onFailure(cause)
+        0 * _._
+    }
+}
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/StopDaemonActionTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/StopDaemonActionTest.groovy
index 91b21f8..151d8d1 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/StopDaemonActionTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/StopDaemonActionTest.groovy
@@ -16,55 +16,17 @@
 package org.gradle.launcher
 
 import spock.lang.Specification
-import org.gradle.logging.internal.OutputEventListener
-import org.gradle.messaging.remote.internal.Connection
-import org.gradle.launcher.protocol.Stop
-import org.gradle.launcher.protocol.CommandComplete
-import org.gradle.initialization.BuildClientMetaData
 
 class StopDaemonActionTest extends Specification {
-    final DaemonConnector connector = Mock()
-    final OutputEventListener outputListener = Mock()
-    final ExecutionListener executionListener = Mock()
-    final BuildClientMetaData clientMetaData = Mock()
-    final StopDaemonAction action = new StopDaemonAction(connector, outputListener, clientMetaData)
+    final DaemonClient client = Mock()
+    final StopDaemonAction action = new StopDaemonAction(client)
 
     def executesStopCommand() {
-        Connection<Object> connection = Mock()
-
-        when:
-        action.execute(executionListener)
-
-        then:
-        1 * connector.maybeConnect() >> connection
-        1 * connection.dispatch({it instanceof Stop})
-        1 * connection.receive() >> new CommandComplete(null)
-        1 * connection.stop()
-        0 * _._
-    }
-
-    def doesNothingWhenDaemonIsNotRunning() {
-        when:
-        action.execute(executionListener)
-
-        then:
-        1 * connector.maybeConnect() >> null
-        0 * _._
-    }
-
-    def reportsFailureToStop() {
-        Connection<Object> connection = Mock()
-        RuntimeException failure = new RuntimeException()
-
         when:
-        action.execute(executionListener)
+        action.run()
 
         then:
-        1 * connector.maybeConnect() >> connection
-        1 * connection.dispatch({it instanceof Stop})
-        1 * connection.receive() >> new CommandComplete(failure)
-        1 * connection.stop()
-        1 * executionListener.onFailure(failure)
+        1 * client.stop()
         0 * _._
     }
 }
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/DaemonGradleLauncherActionExecuterTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/DaemonGradleLauncherActionExecuterTest.groovy
new file mode 100644
index 0000000..ecfbcfa
--- /dev/null
+++ b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/DaemonGradleLauncherActionExecuterTest.groovy
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.provider
+
+import org.gradle.launcher.DaemonClient
+import spock.lang.Specification
+import org.gradle.launcher.ReportedException
+import org.gradle.tooling.internal.protocol.BuildExceptionVersion1
+import org.gradle.initialization.GradleLauncherAction
+import org.gradle.tooling.internal.protocol.BuildOperationParametersVersion1
+
+class DaemonGradleLauncherActionExecuterTest extends Specification {
+    final DaemonClient client = Mock()
+    final GradleLauncherAction<String> action = Mock()
+    final BuildOperationParametersVersion1 parameters = Mock()
+    final DaemonGradleLauncherActionExecuter executer = new DaemonGradleLauncherActionExecuter(client)
+
+    def unpacksReportedException() {
+        def failure = new RuntimeException()
+
+        when:
+        executer.execute(action, parameters)
+
+        then:
+        BuildExceptionVersion1 e = thrown()
+        e.cause == failure
+        1 * client.execute(action, !null) >> { throw new ReportedException(failure) }
+    }
+}
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/EmbeddedGradleLauncherActionExecuterTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/EmbeddedGradleLauncherActionExecuterTest.groovy
new file mode 100644
index 0000000..22d0ca1
--- /dev/null
+++ b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/EmbeddedGradleLauncherActionExecuterTest.groovy
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.provider
+
+import org.gradle.BuildResult
+import org.gradle.GradleLauncher
+import org.gradle.initialization.GradleLauncherAction
+import org.gradle.initialization.GradleLauncherFactory
+import org.gradle.launcher.InitializationAware
+import org.gradle.tooling.internal.protocol.BuildExceptionVersion1
+import org.gradle.tooling.internal.protocol.BuildOperationParametersVersion1
+import spock.lang.Specification
+
+class EmbeddedGradleLauncherActionExecuterTest extends Specification {
+    final BuildOperationParametersVersion1 parameters = Mock()
+    final GradleLauncherFactory gradleLauncherFactory = Mock()
+    final GradleLauncher gradleLauncher = Mock()
+    final BuildResult buildResult = Mock()
+    final EmbeddedGradleLauncherActionExecuter executer = new EmbeddedGradleLauncherActionExecuter(gradleLauncherFactory)
+
+    def executesActionAndReturnsResult() {
+        GradleLauncherAction<String> action = Mock()
+
+        when:
+        def result = executer.execute(action, parameters)
+
+        then:
+        result == 'result'
+        1 * gradleLauncherFactory.newInstance(!null) >> gradleLauncher
+        1 * action.run(gradleLauncher) >> buildResult
+        1 * action.result >> 'result'
+    }
+
+    def actionCanConfigureStartParameters() {
+        TestAction action = Mock()
+
+        when:
+        def result = executer.execute(action, parameters)
+
+        then:
+        result == 'result'
+        1 * gradleLauncherFactory.newInstance(!null) >> gradleLauncher
+        1 * action.configureStartParameter(!null)
+        1 * action.run(gradleLauncher) >> buildResult
+        1 * action.result >> 'result'
+    }
+
+    def wrapsBuildFailure() {
+        GradleLauncherAction<String> action = Mock()
+        RuntimeException failure = new RuntimeException()
+
+        when:
+        executer.execute(action, parameters)
+
+        then:
+        BuildExceptionVersion1 e = thrown()
+        e.cause == failure
+        1 * gradleLauncherFactory.newInstance(!null) >> gradleLauncher
+        1 * action.run(gradleLauncher) >> buildResult
+        buildResult.failure >> failure
+    }
+}
+
+interface TestAction extends GradleLauncherAction<String>, InitializationAware {}
+
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/ExecuteBuildActionTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/ExecuteBuildActionTest.groovy
new file mode 100644
index 0000000..ea73457
--- /dev/null
+++ b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/ExecuteBuildActionTest.groovy
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.provider
+
+import spock.lang.Specification
+import org.gradle.StartParameter
+import org.gradle.GradleLauncher
+import org.gradle.BuildResult
+
+class ExecuteBuildActionTest extends Specification {
+    final ExecuteBuildAction action = new ExecuteBuildAction(['a', 'b'])
+
+    def setsTaskNamesOnStartParameter() {
+        StartParameter startParameter = Mock()
+
+        when:
+        action.configureStartParameter(startParameter)
+
+        then:
+        1 * startParameter.setTaskNames(['a', 'b'])
+        0 * _._
+    }
+
+    def runsBuild() {
+        GradleLauncher launcher = Mock()
+        BuildResult buildResult = Mock()
+
+        when:
+        def result = action.run(launcher)
+
+        then:
+        result == buildResult
+        1 * launcher.run() >> buildResult
+        0 * _._
+    }
+}
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/LoggingBridgingGradleLauncherActionExecuterTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/LoggingBridgingGradleLauncherActionExecuterTest.groovy
new file mode 100644
index 0000000..5938aa4
--- /dev/null
+++ b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/LoggingBridgingGradleLauncherActionExecuterTest.groovy
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.provider
+
+import spock.lang.Specification
+import org.gradle.tooling.internal.protocol.BuildOperationParametersVersion1
+import org.gradle.launcher.GradleLauncherActionExecuter
+import org.gradle.logging.LoggingManagerInternal
+import org.gradle.initialization.GradleLauncherAction
+import org.gradle.api.internal.Factory
+
+class LoggingBridgingGradleLauncherActionExecuterTest extends Specification {
+    final GradleLauncherActionExecuter<BuildOperationParametersVersion1> target = Mock()
+    final Factory<LoggingManagerInternal> loggingManagerFactory = Mock()
+    final LoggingManagerInternal loggingManager = Mock()
+    final GradleLauncherAction<String> action = Mock()
+    final BuildOperationParametersVersion1 parameters = Mock()
+    final LoggingBridgingGradleLauncherActionExecuter executer = new LoggingBridgingGradleLauncherActionExecuter(target, loggingManagerFactory)
+
+    def configuresLoggingWhileActionIsExecuting() {
+        when:
+        executer.execute(action, parameters)
+
+        then:
+        1 * loggingManagerFactory.create() >> loggingManager
+        1 * loggingManager.addOutputEventListener(!null)
+        1 * loggingManager.start()
+        1 * target.execute(action, parameters)
+        1 * loggingManager.stop()
+    }
+
+    def restoresLoggingWhenActionFails() {
+        def failure = new RuntimeException()
+
+        when:
+        executer.execute(action, parameters)
+
+        then:
+        RuntimeException e = thrown()
+        e == failure
+        1 * loggingManagerFactory.create() >> loggingManager
+        1 * loggingManager.start()
+        1 * target.execute(action, parameters) >> {throw failure}
+        1 * loggingManager.stop()
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/plugins/MavenPluginConvention.java b/subprojects/maven/src/main/groovy/org/gradle/api/plugins/MavenPluginConvention.java
index 1488f26..d41b608 100644
--- a/subprojects/maven/src/main/groovy/org/gradle/api/plugins/MavenPluginConvention.java
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/plugins/MavenPluginConvention.java
@@ -16,16 +16,14 @@
 package org.gradle.api.plugins;
 
 import groovy.lang.Closure;
-import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
-import org.gradle.api.artifacts.maven.MavenPom;
-import org.gradle.api.internal.artifacts.publish.maven.DefaultMavenPom;
-import org.gradle.api.internal.artifacts.publish.maven.dependencies.DefaultConf2ScopeMappingContainer;
-import org.gradle.api.internal.artifacts.publish.maven.dependencies.DefaultExcludeRuleConverter;
-import org.gradle.api.internal.artifacts.publish.maven.dependencies.DefaultPomDependenciesConverter;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.maven.*;
+import org.gradle.api.internal.Factory;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.util.ConfigureUtil;
 
 import java.io.File;
+import java.util.Collections;
 
 /**
  * Properties and methods added by the {@link org.gradle.api.plugins.MavenPlugin}.
@@ -33,12 +31,15 @@ import java.io.File;
  * @author Hans Dockter
  */
 public class MavenPluginConvention {
-    private ProjectInternal project;
+    private final ProjectInternal project;
+    private final MavenFactory mavenFactory;
+    private Conf2ScopeMappingContainer conf2ScopeMappings;
     private String pomDirName = "poms";
-    private Conf2ScopeMappingContainer conf2ScopeMappings = new DefaultConf2ScopeMappingContainer();
 
     public MavenPluginConvention(ProjectInternal project) {
         this.project = project;
+        mavenFactory = project.getServices().get(MavenFactory.class);
+        conf2ScopeMappings = mavenFactory.createConf2ScopeMappingContainer(Collections.<Configuration, Conf2ScopeMapping>emptyMap());
     }
 
     /**
@@ -91,10 +92,10 @@ public class MavenPluginConvention {
      * @return The POM instance.
      */
     public MavenPom pom(Closure configureClosure) {
-        DefaultMavenPom pom = new DefaultMavenPom(project.getConfigurations(),
-                new DefaultConf2ScopeMappingContainer(conf2ScopeMappings.getMappings()),
-                new DefaultPomDependenciesConverter(new DefaultExcludeRuleConverter()),
+        Factory<MavenPom> pomFactory = mavenFactory.createMavenPomFactory(project.getConfigurations(),
+                conf2ScopeMappings.getMappings(),
                 project.getFileResolver());
+        MavenPom pom = pomFactory.create();
         pom.setGroupId(project.getGroup().toString());
         pom.setArtifactId(project.getName());
         pom.setVersion(project.getVersion().toString());
diff --git a/subprojects/maven/src/main/resources/META-INF/gradle-plugins/maven.properties b/subprojects/maven/src/main/resources/META-INF/gradle-plugins/maven.properties
index 8985224..3d889a7 100644
--- a/subprojects/maven/src/main/resources/META-INF/gradle-plugins/maven.properties
+++ b/subprojects/maven/src/main/resources/META-INF/gradle-plugins/maven.properties
@@ -1,16 +1 @@
-#
-# Copyright 2010 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-implementation-class=org.gradle.api.plugins.MavenPlugin
+implementation-class=org.gradle.api.plugins.MavenPlugin
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/plugins/MavenPluginConventionTest.groovy b/subprojects/maven/src/test/groovy/org/gradle/api/plugins/MavenPluginConventionTest.groovy
index 5083b0d..114a4ca 100644
--- a/subprojects/maven/src/test/groovy/org/gradle/api/plugins/MavenPluginConventionTest.groovy
+++ b/subprojects/maven/src/test/groovy/org/gradle/api/plugins/MavenPluginConventionTest.groovy
@@ -15,10 +15,10 @@
  */
 package org.gradle.api.plugins
 
-import org.gradle.api.internal.artifacts.publish.maven.DefaultMavenPom
-import org.gradle.api.internal.artifacts.publish.maven.dependencies.DefaultConf2ScopeMappingContainer
 import org.gradle.api.internal.project.DefaultProject
 import org.gradle.util.HelperUtil
+import org.gradle.api.artifacts.maven.MavenPom
+
 import spock.lang.Specification
 
 /**
@@ -29,10 +29,9 @@ class MavenPluginConventionTest extends Specification {
     MavenPluginConvention mavenPluginConvention = new MavenPluginConvention(project)
 
     def pomShouldCreateMavenPom() {
-        mavenPluginConvention.conf2ScopeMappings = new DefaultConf2ScopeMappingContainer();
         project.group = 'someGroup'
         project.version = '1.0'
-        DefaultMavenPom mavenPom = mavenPluginConvention.pom()
+        MavenPom mavenPom = mavenPluginConvention.pom()
 
         expect:
         !mavenPluginConvention.conf2ScopeMappings.is(mavenPom.scopeMappings)
@@ -47,8 +46,7 @@ class MavenPluginConventionTest extends Specification {
     }
 
     def pomShouldCreateAndConfigureMavenPom() {
-        mavenPluginConvention.conf2ScopeMappings = new DefaultConf2ScopeMappingContainer();
-        DefaultMavenPom mavenPom = mavenPluginConvention.pom {
+        MavenPom mavenPom = mavenPluginConvention.pom {
             project {
                 inceptionYear '1999'
             }
diff --git a/subprojects/open-api/open-api.gradle b/subprojects/open-api/open-api.gradle
index 7971c23..88c9c91 100644
--- a/subprojects/open-api/open-api.gradle
+++ b/subprojects/open-api/open-api.gradle
@@ -11,13 +11,3 @@ dependencies {
     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.gradleUserHomeDir'] = rootProject.integTest.integTestUserDir.absolutePath
-    testClassesDir = sourceSets.integTest.classesDir
-    classpath = sourceSets.integTest.runtimeClasspath + configurations.integTestRuntime
-
-    doFirst { task -> systemProperties['integTest.gradleHomeDir'] = rootProject.intTestImage.integTestGradleHome.absolutePath }
-}
diff --git a/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/CrossVersionCompatibilityIntegrationTest.groovy b/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/CrossVersionCompatibilityIntegrationTest.groovy
index 599450f..4785598 100644
--- a/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/CrossVersionCompatibilityIntegrationTest.groovy
+++ b/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/CrossVersionCompatibilityIntegrationTest.groovy
@@ -31,22 +31,23 @@ class CrossVersionCompatibilityIntegrationTest {
     @Rule public final GradleDistribution dist = new GradleDistribution()
     @Rule public final TestResources resources = new TestResources()
 
-    private final BasicGradleDistribution gradle09rc1 = dist.previousVersion('0.9-rc-1')
-    private final BasicGradleDistribution gradle09rc2 = dist.previousVersion('0.9-rc-2')
     private final BasicGradleDistribution gradle09rc3 = dist.previousVersion('0.9-rc-3')
     private final BasicGradleDistribution gradle09 = dist.previousVersion('0.9')
     private final BasicGradleDistribution gradle091 = dist.previousVersion('0.9.1')
+    private final BasicGradleDistribution gradle092 = dist.previousVersion('0.9.2')
+    private final BasicGradleDistribution gradle10Milestone1 = dist.previousVersion('1.0-milestone-1')
+    private final BasicGradleDistribution gradle10Milestone2 = dist.previousVersion('1.0-milestone-2')
 
     @Test
     public void canUseOpenApiFromCurrentVersionToBuildUsingAnOlderVersion() {
-        [gradle09rc1, gradle09rc2, gradle09rc3, gradle09, gradle091].each {
+        [gradle09rc3, gradle09, gradle091, gradle092, gradle10Milestone1, gradle10Milestone2].each {
             checkCanBuildUsing(dist, it)
         }
     }
 
     @Test
     public void canUseOpenApiFromOlderVersionToBuildUsingCurrentVersion() {
-        [gradle09rc1, gradle09rc2, gradle09rc3, gradle09, gradle091].each {
+        [gradle09rc3, gradle09, gradle091, gradle092, gradle10Milestone1, gradle10Milestone2].each {
             checkCanBuildUsing(it, dist)
         }
     }
diff --git a/subprojects/osgi/osgi.gradle b/subprojects/osgi/osgi.gradle
index bd6713b..8bb9536 100644
--- a/subprojects/osgi/osgi.gradle
+++ b/subprojects/osgi/osgi.gradle
@@ -13,9 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-repositories {
-    mavenRepo urls: 'http://www.aQute.biz/repo'
-}
 
 dependencies {
     groovy libraries.groovy_depends
@@ -24,7 +21,7 @@ dependencies {
     compile project(':plugins')
     compile libraries.slf4j_api
 
-    compile 'biz.aQute:bndlib:0.0.384 at jar'
+    compile 'biz.aQute:bndlib:1.15.0 at jar'
 
     testCompile project(path: ':core', configuration: 'testFixtures')
     testRuntime project(path: ':core', configuration: 'testFixturesRuntime')
diff --git a/subprojects/osgi/src/main/groovy/org/gradle/api/internal/plugins/osgi/DefaultOsgiManifest.java b/subprojects/osgi/src/main/groovy/org/gradle/api/internal/plugins/osgi/DefaultOsgiManifest.java
index 8358718..b30a25c 100644
--- a/subprojects/osgi/src/main/groovy/org/gradle/api/internal/plugins/osgi/DefaultOsgiManifest.java
+++ b/subprojects/osgi/src/main/groovy/org/gradle/api/internal/plugins/osgi/DefaultOsgiManifest.java
@@ -31,7 +31,6 @@ import java.io.IOException;
 import java.util.*;
 import java.util.jar.Manifest;
 
-
 /**
  * @author Hans Dockter
  */
@@ -67,7 +66,7 @@ public class DefaultOsgiManifest extends DefaultManifest implements OsgiManifest
                 effectiveManifest.attributes(WrapUtil.toMap(entry.getKey().toString(), (String) entry.getValue()));
             }
             effectiveManifest.attributes(this.getAttributes());
-            for(Map.Entry<String, Attributes> ent : getSections().entrySet()) {
+            for (Map.Entry<String, Attributes> ent : getSections().entrySet()) {
                 effectiveManifest.attributes(ent.getValue(), ent.getKey());
             }
         } catch (Exception e) {
@@ -77,9 +76,25 @@ public class DefaultOsgiManifest extends DefaultManifest implements OsgiManifest
     }
 
     private void setAnalyzerProperties(Analyzer analyzer) throws IOException {
-        for (String instructionName : instructions.keySet()) {
-            analyzer.setProperty(instructionName, createPropertyStringFromList(instructionValue(instructionName)));
+        for (Map.Entry<String, Object> attribute : getAttributes().entrySet()) {
+            String key = attribute.getKey();
+            if (!"Manifest-Version".equals(key)) {
+                analyzer.setProperty(key, attribute.getValue().toString());
+            }
+        }
+        Set<String> instructionNames = instructions.keySet();
+        if (!instructionNames.contains(Analyzer.IMPORT_PACKAGE)) {
+            analyzer.setProperty(Analyzer.IMPORT_PACKAGE,
+                    "*, !org.apache.ant.*, !org.junit.*, !org.jmock.*, !org.easymock.*, !org.mockito.*");
         }
+        if (!instructionNames.contains(Analyzer.EXPORT_PACKAGE)) {
+            analyzer.setProperty(Analyzer.EXPORT_PACKAGE, "*;-noimport:=false;version=" + version);
+        }
+        for (String instructionName : instructionNames) {
+            String list = createPropertyStringFromList(instructionValue(instructionName));
+            analyzer.setProperty(instructionName, list);
+        }
+
         setProperty(analyzer, Analyzer.BUNDLE_VERSION, getVersion());
         setProperty(analyzer, Analyzer.BUNDLE_SYMBOLICNAME, getSymbolicName());
         setProperty(analyzer, Analyzer.BUNDLE_NAME, getName());
diff --git a/subprojects/osgi/src/main/groovy/org/gradle/api/internal/plugins/osgi/OsgiHelper.java b/subprojects/osgi/src/main/groovy/org/gradle/api/internal/plugins/osgi/OsgiHelper.java
index 114d812..f82b53f 100644
--- a/subprojects/osgi/src/main/groovy/org/gradle/api/internal/plugins/osgi/OsgiHelper.java
+++ b/subprojects/osgi/src/main/groovy/org/gradle/api/internal/plugins/osgi/OsgiHelper.java
@@ -15,9 +15,11 @@
  */
 package org.gradle.api.internal.plugins.osgi;
 
+import org.gradle.api.GradleException;
 import org.gradle.api.Project;
 import org.gradle.api.plugins.BasePluginConvention;
 
+import java.util.StringTokenizer;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -28,42 +30,45 @@ public class OsgiHelper {
     /**
      * Bundle-Version must match this pattern
      */
-    private static final Pattern OSGI_VERSION_PATTERN = Pattern
-            .compile("[0-9]+\\.[0-9]+\\.[0-9]+(\\.[0-9A-Za-z_-]+)?");
+    private static final Pattern OSGI_VERSION_PATTERN = Pattern.compile("[0-9]+(\\.[0-9]+(\\.[0-9]+(\\.[0-9A-Za-z_-]+)?)?)?");
 
-    /** pattern used to change - to . */
-    // private static final Pattern P_VERSION = Pattern.compile("([0-9]+(\\.[0-9])*)-(.*)");
-    /**
-     * pattern that matches strings that contain only numbers
-     */
     private static final Pattern ONLY_NUMBERS = Pattern.compile("[0-9]+");
-    private static final Pattern DATED_SNAPSHOT = Pattern.compile("([0-9])(\\.([0-9]))?(\\.([0-9]))?\\-([0-9]{8}\\.[0-9]{6}\\-[0-9]*)");
-    private static final Pattern DOTS_IN_QUALIFIER = Pattern.compile("([0-9])(\\.[0-9])?\\.([0-9A-Za-z_-]+)\\.([0-9A-Za-z_-]+)");
-    private static final Pattern NEED_TO_FILL_ZEROS = Pattern.compile("([0-9])(\\.([0-9]))?(\\.([0-9A-Za-z_-]+))?");
-
-    private String getBundleSymbolicName(String groupId, String artifactId) {
-        return groupId + "." + artifactId;
-    }
+    private static final Pattern QUALIFIER = Pattern.compile("[0-9A-Za-z_\\-]*");
 
     /**
      * Get the symbolic name as group + "." + archivesBaseName, with the following exceptions
      * <ul>
-     * <li>if group has only one section (no dots) and archivesBaseName is not null then the
-     * first package name with classes is returned. eg. commons-logging:commons-logging ->
-     * org.apache.commons.logging</li>
-     * <li>if archivesBaseName is equal to last section of group then group is returned. eg.
-     * org.gradle:gradle -> org.gradle</li>
-     * <li>if archivesBaseName starts with last section of group that portion is removed. eg.
-     * org.gradle:gradle-core -> org.gradle.core</li>
+     * <li>
+     * if group has only one section (no dots) and archivesBaseName is not null then the first package
+     * name with classes is returned. eg. commons-logging:commons-logging -> org.apache.commons.logging
+     * </li>
+     * <li>
+     * if archivesBaseName is equal to last section of group then group is returned.
+     * eg. org.gradle:gradle -> org.gradle
+     * </li>
+     * <li>
+     * if archivesBaseName starts with last section of group that portion is removed.
+     * eg. org.gradle:gradle-core -> org.gradle.core
+     * </li>
+     * <li>
+     * if archivesBaseName starts with the full group, the archivesBaseName is return,
+     * e.g. org.gradle:org.gradle.core -> org.gradle.core
+     * </li>
      * </ul>
+     *
+     * @param project The project being processed.
+     *
+     * @return Returns the SymbolicName that should be used for the bundle.
      */
     public String getBundleSymbolicName(Project project) {
 
         String group = (String) project.property("group");
+        String archiveBaseName = project.getConvention().getPlugin(BasePluginConvention.class).getArchivesBaseName();
+        if (archiveBaseName.startsWith(group)) {
+            return archiveBaseName;
+        }
         int i = group.lastIndexOf('.');
-
         String lastSection = group.substring(++i);
-        String archiveBaseName = project.getConvention().getPlugin(BasePluginConvention.class).getArchivesBaseName();
         if (archiveBaseName.equals(lastSection)) {
             return group;
         }
@@ -77,120 +82,74 @@ public class OsgiHelper {
         }
         return getBundleSymbolicName(group, archiveBaseName);
     }
-    
-    public String getVersion(String version) {
-        String osgiVersion;
-
-        // Matcher m = P_VERSION.matcher(version);
-        // if (m.matches()) {
-        // osgiVersion = m.group(1) + "." + m.group(3);
-        // }
 
-        /* TODO need a regexp guru here */
+    private String getBundleSymbolicName(String groupId, String artifactId) {
+        return groupId + "." + artifactId;
+    }
 
-        Matcher m;
+    public String getVersion(String version) {
 
         /* if it's already OSGi compliant don't touch it */
-        m = OSGI_VERSION_PATTERN.matcher(version);
+        final Matcher m = OSGI_VERSION_PATTERN.matcher(version);
         if (m.matches()) {
             return version;
         }
 
-        osgiVersion = version;
-
-        /* check for dated snapshot versions with only major or major and minor */
-        m = DATED_SNAPSHOT.matcher(osgiVersion);
-        if (m.matches()) {
-            String major = m.group(1);
-            String minor = (m.group(3) != null) ? m.group(3) : "0";
-            String service = (m.group(5) != null) ? m.group(5) : "0";
-            String qualifier = m.group(6).replaceAll("-", "_").replaceAll("\\.", "_");
-            osgiVersion = major + "." + minor + "." + service + "." + qualifier;
-        }
-
-        /* else transform first - to . and others to _ */
-        osgiVersion = osgiVersion.replaceFirst("-", "\\.");
-        osgiVersion = osgiVersion.replaceAll("-", "_");
-        m = OSGI_VERSION_PATTERN.matcher(osgiVersion);
-        if (m.matches()) {
-            return osgiVersion;
-        }
-
-        /* remove dots in the middle of the qualifier */
-        m = DOTS_IN_QUALIFIER.matcher(osgiVersion);
-        if (m.matches()) {
-            String s1 = m.group(1);
-            String s2 = m.group(2);
-            String s3 = m.group(3);
-            String s4 = m.group(4);
-
-            Matcher qualifierMatcher = ONLY_NUMBERS.matcher(s3);
-            /*
-             * if last portion before dot is only numbers then it's not in the middle of the
-             * qualifier
-             */
-            if (!qualifierMatcher.matches()) {
-                osgiVersion = s1 + s2 + "." + s3 + "_" + s4;
-            }
-        }
-
-        /* convert
-         * 1.string   -> 1.0.0.string
-         * 1.2.string -> 1.2.0.string
-         * 1          -> 1.0.0
-         * 1.1        -> 1.1.0
-         */
-        //Pattern NEED_TO_FILL_ZEROS = Pattern.compile( "([0-9])(\\.([0-9]))?\\.([0-9A-Za-z_-]+)" );
-        m = NEED_TO_FILL_ZEROS.matcher(osgiVersion);
-        if (m.matches()) {
-            String major = m.group(1);
-            String minor = m.group(3);
-            String service = null;
-            String qualifier = m.group(5);
-
-            /* if there's no qualifier just fill with 0s */
-            if (qualifier == null) {
-                osgiVersion = getVersion(major, minor, service, qualifier);
-            } else {
-                /* if last portion is only numbers then it's not a qualifier */
-                Matcher qualifierMatcher = ONLY_NUMBERS.matcher(qualifier);
-                if (qualifierMatcher.matches()) {
-                    if (minor == null) {
-                        minor = qualifier;
+        int group = 0;
+        boolean groupToken = true;
+        String[] groups = new String[4];
+        groups[0] = "0";
+        groups[1] = "0";
+        groups[2] = "0";
+        groups[3] = "";
+        StringTokenizer st = new StringTokenizer(version, ",./;'?:\\|=+-_*&^%$#@!~", true);
+        while (st.hasMoreTokens()) {
+            String token = st.nextToken();
+            if (groupToken) {
+                if (group < 3) {
+                    if (ONLY_NUMBERS.matcher(token).matches()) {
+                        groups[group++] = token;
+                        groupToken = false;
                     } else {
-                        service = qualifier;
+                        // if not a number, i.e. 2.ABD
+                        groups[3] = token + fillQualifier(st);
                     }
-                    osgiVersion = getVersion(major, minor, service, null);
                 } else {
-                    osgiVersion = getVersion(major, minor, service, qualifier);
+                    // Last group; what ever is left take that replace all characters that are not alphanum or '_' or '-'
+                    groups[3] = token + fillQualifier(st);
+                }
+            } else {
+                // If a delimiter; if dot, swap to groupToken, otherwise the rest belongs in qualifier.
+                if (".".equals(token)) {
+                    groupToken = true;
+                } else {
+                    groups[3] = fillQualifier(st);
                 }
             }
         }
-
-        m = OSGI_VERSION_PATTERN.matcher(osgiVersion);
-        /* if still its not OSGi version then add everything as qualifier */
-        if (!m.matches()) {
-            String major = "0";
-            String minor = "0";
-            String service = "0";
-            String qualifier = osgiVersion.replaceAll("\\.", "_");
-            osgiVersion = major + "." + minor + "." + service + "." + qualifier;
+        String ver = groups[0] + "." + groups[1] + "." + groups[2];
+        String result;
+        if (groups[3].length() > 0) {
+            result = ver + "." + groups[3];
+        } else {
+            result = ver;
         }
-
-        return osgiVersion;
+        if (!OSGI_VERSION_PATTERN.matcher(result).matches()) {
+            throw new GradleException("OSGi plugin unable to convert version to a compliant version");
+        }
+        return result;
     }
 
-    private String getVersion(String major, String minor, String service, String qualifier) {
-        StringBuffer sb = new StringBuffer();
-        sb.append(major != null ? major : "0");
-        sb.append('.');
-        sb.append(minor != null ? minor : "0");
-        sb.append('.');
-        sb.append(service != null ? service : "0");
-        if (qualifier != null) {
-            sb.append('.');
-            sb.append(qualifier);
+    private String fillQualifier(StringTokenizer st) {
+        StringBuffer buf = new StringBuffer();
+        while (st.hasMoreTokens()) {
+            String token = st.nextToken();
+            if (QUALIFIER.matcher(token).matches()) {
+                buf.append(token);
+            } else {
+                buf.append("_");
+            }
         }
-        return sb.toString();
+        return buf.toString();
     }
 }
diff --git a/subprojects/osgi/src/main/groovy/org/gradle/api/plugins/osgi/OsgiManifest.java b/subprojects/osgi/src/main/groovy/org/gradle/api/plugins/osgi/OsgiManifest.java
index 9c20193..4ecdf8f 100644
--- a/subprojects/osgi/src/main/groovy/org/gradle/api/plugins/osgi/OsgiManifest.java
+++ b/subprojects/osgi/src/main/groovy/org/gradle/api/plugins/osgi/OsgiManifest.java
@@ -51,8 +51,8 @@ public interface OsgiManifest extends org.gradle.api.java.archives.Manifest {
      * Adds arguments to an instruction. If the instruction does not exists, it is created. If it does exists, the
      * arguments are inserted before the existing arguments.
      *
-     * @param name
-     * @param values
+     * @param name Name of the instruction.
+     * @param values The values for the instruction.
      * @return this
      * @see #instructionFirst(String, String...)
      */
@@ -68,7 +68,8 @@ public interface OsgiManifest extends org.gradle.api.java.archives.Manifest {
     /**
      * Returns the symbolic name.
      *
-     * @see #setSymbolicName(String) 
+     * @see #setSymbolicName(String)
+     * @return the symbolic name.
      */
     String getSymbolicName();
 
diff --git a/subprojects/osgi/src/main/groovy/org/gradle/api/plugins/osgi/OsgiPlugin.groovy b/subprojects/osgi/src/main/groovy/org/gradle/api/plugins/osgi/OsgiPlugin.groovy
index 6d8b65a..04292d0 100644
--- a/subprojects/osgi/src/main/groovy/org/gradle/api/plugins/osgi/OsgiPlugin.groovy
+++ b/subprojects/osgi/src/main/groovy/org/gradle/api/plugins/osgi/OsgiPlugin.groovy
@@ -13,32 +13,31 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.gradle.api.plugins.osgi;
+package org.gradle.api.plugins.osgi
 
 
 import org.gradle.api.Plugin
 import org.gradle.api.Project
 import org.gradle.api.plugins.JavaBasePlugin
 import org.gradle.api.plugins.JavaPlugin
-import org.gradle.api.tasks.SourceSet
 
 /**
- * <p>A {@link Plugin} which extends the {@link JavaPlugin} to add OSGi meta-information to the project JARs.</p>
+ * A {@link Plugin} which extends the {@link JavaPlugin} to add OSGi meta-information to the project Jars.
  *
  * @author Hans Dockter
  */
 public class OsgiPlugin implements Plugin<Project> {
     public void apply(Project project) {
-        project.getPlugins().apply(JavaBasePlugin.class);
+        project.plugins.apply(JavaBasePlugin)
 
-        OsgiPluginConvention osgiConvention = new OsgiPluginConvention(project);
+        def osgiConvention = new OsgiPluginConvention(project)
         project.convention.plugins.osgi = osgiConvention
 
-        project.plugins.withType(JavaPlugin.class) {javaPlugin ->
-            OsgiManifest osgiManifest = osgiConvention.osgiManifest {
+        project.plugins.withType(JavaPlugin) {
+            def osgiManifest = osgiConvention.osgiManifest {
                 from project.manifest
-                classesDir = project.convention.plugins.java.sourceSets[SourceSet.MAIN_SOURCE_SET_NAME].classesDir
-                classpath = project.configurations[JavaPlugin.RUNTIME_CONFIGURATION_NAME]
+                classesDir = project.sourceSets.main.classesDir
+                classpath = project.configurations.runtime
             }
             project.jar.manifest = osgiManifest
         }
diff --git a/subprojects/osgi/src/main/resources/META-INF/gradle-plugins/osgi.properties b/subprojects/osgi/src/main/resources/META-INF/gradle-plugins/osgi.properties
index 153189d..551d922 100644
--- a/subprojects/osgi/src/main/resources/META-INF/gradle-plugins/osgi.properties
+++ b/subprojects/osgi/src/main/resources/META-INF/gradle-plugins/osgi.properties
@@ -1,16 +1 @@
-#
-# Copyright 2010 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-implementation-class=org.gradle.api.plugins.osgi.OsgiPlugin
+implementation-class=org.gradle.api.plugins.osgi.OsgiPlugin
diff --git a/subprojects/osgi/src/test/groovy/org/gradle/api/internal/plugins/osgi/DefaultOsgiManifestTest.java b/subprojects/osgi/src/test/groovy/org/gradle/api/internal/plugins/osgi/DefaultOsgiManifestTest.java
index a0c5d02..60d085c 100644
--- a/subprojects/osgi/src/test/groovy/org/gradle/api/internal/plugins/osgi/DefaultOsgiManifestTest.java
+++ b/subprojects/osgi/src/test/groovy/org/gradle/api/internal/plugins/osgi/DefaultOsgiManifestTest.java
@@ -186,8 +186,8 @@ public class DefaultOsgiManifestTest {
         osgiManifest.setLicense("myLicense");
         osgiManifest.setVendor("myVendor");
         osgiManifest.setDocURL("myDocUrl");
-        osgiManifest.instruction(Analyzer.EXPORT_PACKAGE, new String[]{"pack1", "pack2"});
-        osgiManifest.instruction(Analyzer.IMPORT_PACKAGE, new String[]{"pack3", "pack4"});
+        osgiManifest.instruction(Analyzer.EXPORT_PACKAGE, "pack1", "pack2");
+        osgiManifest.instruction(Analyzer.IMPORT_PACKAGE, "pack3", "pack4");
         osgiManifest.setClasspath(fileCollection);
         osgiManifest.setClassesDir(new File("someDir"));
         addPlainAttributesAndSections(osgiManifest);
@@ -210,6 +210,9 @@ public class DefaultOsgiManifestTest {
             one(analyzerMock).setProperty(Analyzer.BUNDLE_DOCURL, osgiManifest.getDocURL());
             one(analyzerMock).setProperty(Analyzer.EXPORT_PACKAGE, GUtil.join(osgiManifest.instructionValue(Analyzer.EXPORT_PACKAGE), ","));
             one(analyzerMock).setProperty(Analyzer.IMPORT_PACKAGE, GUtil.join(osgiManifest.instructionValue(Analyzer.IMPORT_PACKAGE), ","));
+
+            one(analyzerMock).setProperty(ARBITRARY_ATTRIBUTE, "I like green eggs and ham.");
+
             one(analyzerMock).setJar(osgiManifest.getClassesDir());
             one(analyzerMock).setClasspath(osgiManifest.getClasspath().getFiles().toArray(new File[osgiManifest.getClasspath().getFiles().size()]));
             Manifest testManifest = new Manifest();
diff --git a/subprojects/osgi/src/test/groovy/org/gradle/api/internal/plugins/osgi/OsgiHelperTest.groovy b/subprojects/osgi/src/test/groovy/org/gradle/api/internal/plugins/osgi/OsgiHelperTest.groovy
new file mode 100644
index 0000000..113f7e8
--- /dev/null
+++ b/subprojects/osgi/src/test/groovy/org/gradle/api/internal/plugins/osgi/OsgiHelperTest.groovy
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+import spock.lang.Specification
+
+public class OsgiHelperTest extends Specification {
+    def "convert to OSGi-compliant version"() {
+        def helper = new OsgiHelper()
+
+        expect:
+        helper.getVersion(projectVersion) == osgiVersion
+
+        where:
+        projectVersion   | osgiVersion
+        "1"              | "1"
+        "1.2"            | "1.2"
+        "1.2.3"          | "1.2.3"
+        "1.2.3.4"        | "1.2.3.4"
+        "1.2.3.4.5"      | "1.2.3.4_5"
+        "1.2.3.4.5.6"    | "1.2.3.4_5_6"
+        "1.2.3.4.5.6.7"  | "1.2.3.4_5_6_7"
+        "1.2.3.4.5-6.7"  | "1.2.3.4_5-6_7"
+        "1.2.3.4-5.6.7"  | "1.2.3.4-5_6_7"
+        "1.2.3.ABC"      | "1.2.3.ABC"
+        "1.2.ABC"        | "1.2.0.ABC"
+        "1.ABC"          | "1.0.0.ABC"
+        "1-ABC"          | "1.0.0.ABC"
+        "1-20110303"     | "1.0.0.20110303"
+        "1.2-20110303"   | "1.2.0.20110303"
+        "1.2.3-20110303" | "1.2.3.20110303"
+        "1_20110303"     | "1.0.0.20110303"
+        "1.2_20110303"   | "1.2.0.20110303"
+        "1.2.3_20110303" | "1.2.3.20110303"
+        "1.2.3_20110303" | "1.2.3.20110303"
+        "1*20110303"     | "1.0.0.20110303"
+        "1*20110303"     | "1.0.0.20110303"
+        "1.2*20110303"   | "1.2.0.20110303"
+        "1.2.3*20110303" | "1.2.3.20110303"
+        "1 at 20110303"     | "1.0.0.20110303"
+        "1.2 at 20110303"   | "1.2.0.20110303"
+        "1.2.3 at 20110303" | "1.2.3.20110303"
+        "1!20110303"     | "1.0.0.20110303"
+        "1.2!20110303"   | "1.2.0.20110303"
+        "1.2.3!20110303" | "1.2.3.20110303"
+        "1%20110303"     | "1.0.0.20110303"
+        "1.2%20110303"   | "1.2.0.20110303"
+        "1.2.3%20110303" | "1.2.3.20110303"
+        "1^20110303"     | "1.0.0.20110303"
+        "1.2^20110303"   | "1.2.0.20110303"
+        "1.2.3^20110303" | "1.2.3.20110303"
+        "1/20110303"     | "1.0.0.20110303"
+        "1.2/20110303"   | "1.2.0.20110303"
+        "1.2.3/20110303" | "1.2.3.20110303"
+        '1-20.11$03 at 0#3' | "1.0.0.20_11_03_0_3"
+        "100000"         | "100000"
+    }
+}
diff --git a/subprojects/plugins/plugins.gradle b/subprojects/plugins/plugins.gradle
index c8dda61..e61774e 100644
--- a/subprojects/plugins/plugins.gradle
+++ b/subprojects/plugins/plugins.gradle
@@ -1,3 +1,5 @@
+import org.gradle.api.internal.artifacts.dependencies.DefaultSelfResolvingDependency
+
 /*
  * Copyright 2010 the original author or authors.
  *
@@ -15,6 +17,7 @@
  */
 
 configurations {
+    reports
     testFixtures
 }
 
@@ -27,22 +30,45 @@ dependencies {
             libraries.commons_lang,
             libraries.asm_all,
             libraries.junit,
-            libraries.ant_junit,
             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'
+            'org.testng:testng:5.14.10'
 
-    testCompile libraries.xmlunit
+    reports 'css3-pie:css3-pie:1.0beta3'
+    testCompile libraries.xmlunit, 'net.sourceforge.nekohtml:nekohtml:1.9.14'
 
     testCompile project(path: ':core', configuration: 'testFixtures')
     testRuntime project(path: ':core', configuration: 'testFixturesRuntime')
     testFixtures sourceSets.test.classes
 }
 
+task reportResources << {
+    copy {
+        from(configurations.reports)
+        into "${sourceSets.main.classesDir}/org/gradle/api/internal/tasks/testing/junit/report"
+    }
+}
+
+classes.dependsOn reportResources
+
+task ideResources(type: Copy) {
+    from(configurations.reports)
+    into "${ideDir}/resources/test/org/gradle/api/internal/tasks/testing/junit/report"
+}
+
+ide.dependsOn ideResources
+
+ideaModule {
+    dependsOn ideResources
+    scopes.RUNTIME.plus.add(configurations.detachedConfiguration(new DefaultSelfResolvingDependency(files(new File(ideDir, "resources/test/")))))
+}
+
+eclipseClasspath {
+    dependsOn ideResources
+    plusConfigurations.add(configurations.detachedConfiguration(new DefaultSelfResolvingDependency(files(new File(ideDir, "resources/test/")))))
+}
+
 test {
     exclude 'org/gradle/api/internal/tasks/testing/junit/ATestClass*.*'
+    exclude 'org/gradle/api/internal/tasks/testing/junit/ABroken*TestClass*.*'
     jvmArgs '-Xms128m', '-Xmx256m', '-XX:+HeapDumpOnOutOfMemoryError'
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/DefaultGroovySourceSet.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/DefaultGroovySourceSet.java
index f215490..f4ec904 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/DefaultGroovySourceSet.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/DefaultGroovySourceSet.java
@@ -15,27 +15,23 @@
  */
 package org.gradle.api.internal.tasks;
 
-import org.gradle.api.tasks.GroovySourceSet;
-import org.gradle.api.file.FileTree;
+import groovy.lang.Closure;
 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.api.tasks.GroovySourceSet;
 import org.gradle.util.ConfigureUtil;
-import groovy.lang.Closure;
 
 public class DefaultGroovySourceSet implements GroovySourceSet {
     private final SourceDirectorySet groovy;
-    private final UnionFileTree allGroovy;
-    private final PatternFilterable groovyPatterns = new PatternSet();
+    private final SourceDirectorySet allGroovy;
 
     public DefaultGroovySourceSet(String displayName, FileResolver fileResolver) {
         groovy = new DefaultSourceDirectorySet(String.format("%s Groovy source", displayName), fileResolver);
         groovy.getFilter().include("**/*.java", "**/*.groovy");
-        groovyPatterns.include("**/*.groovy");
-        allGroovy = new UnionFileTree(String.format("%s Groovy source", displayName), groovy.matching(groovyPatterns));
+        allGroovy = new DefaultSourceDirectorySet(String.format("%s Groovy source", displayName), fileResolver);
+        allGroovy.source(groovy);
+        allGroovy.getFilter().include("**/*.groovy");
     }
 
     public SourceDirectorySet getGroovy() {
@@ -47,11 +43,7 @@ public class DefaultGroovySourceSet implements GroovySourceSet {
         return this;
     }
 
-    public PatternFilterable getGroovySourcePatterns() {
-        return groovyPatterns;
-    }
-
-    public FileTree getAllGroovy() {
+    public SourceDirectorySet getAllGroovy() {
         return allGroovy;
     }
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/DefaultSourceSet.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/DefaultSourceSet.java
index 2d0a70b..4a46e78 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/DefaultSourceSet.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/DefaultSourceSet.java
@@ -16,19 +16,17 @@
 package org.gradle.api.internal.tasks;
 
 import groovy.lang.Closure;
+import org.apache.commons.lang.StringUtils;
 import org.gradle.api.file.FileCollection;
-import org.gradle.api.file.FileTree;
-import org.gradle.api.file.SourceDirectorySet;
 import org.gradle.api.file.FileTreeElement;
+import org.gradle.api.file.SourceDirectorySet;
 import org.gradle.api.internal.file.DefaultSourceDirectorySet;
 import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.internal.file.PathResolvingFileCollection;
-import org.gradle.api.internal.file.UnionFileTree;
-import org.gradle.api.tasks.SourceSet;
+import org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection;
 import org.gradle.api.specs.Spec;
+import org.gradle.api.tasks.SourceSet;
 import org.gradle.util.ConfigureUtil;
 import org.gradle.util.GUtil;
-import org.apache.commons.lang.StringUtils;
 
 import java.io.File;
 import java.util.concurrent.Callable;
@@ -40,11 +38,11 @@ public class DefaultSourceSet implements SourceSet {
     private FileCollection compileClasspath;
     private FileCollection runtimeClasspath;
     private final SourceDirectorySet javaSource;
-    private final UnionFileTree allJavaSource;
+    private final SourceDirectorySet allJavaSource;
     private final SourceDirectorySet resources;
-    private final PathResolvingFileCollection classes;
+    private final DefaultConfigurableFileCollection classes;
     private final String displayName;
-    private final UnionFileTree allSource;
+    private final SourceDirectorySet allSource;
 
     public DefaultSourceSet(String name, FileResolver fileResolver, TaskResolver taskResolver) {
         this.name = name;
@@ -52,10 +50,13 @@ public class DefaultSourceSet implements SourceSet {
         displayName = GUtil.toWords(this.name);
 
         String javaSrcDisplayName = String.format("%s Java source", displayName);
+
         javaSource = new DefaultSourceDirectorySet(javaSrcDisplayName, fileResolver);
         javaSource.getFilter().include("**/*.java");
 
-        allJavaSource = new UnionFileTree(javaSrcDisplayName, javaSource.matching(javaSource.getFilter()));
+        allJavaSource = new DefaultSourceDirectorySet(javaSrcDisplayName, fileResolver);
+        allJavaSource.getFilter().include("**/*.java");
+        allJavaSource.source(javaSource);
 
         String resourcesDisplayName = String.format("%s resources", displayName);
         resources = new DefaultSourceDirectorySet(resourcesDisplayName, fileResolver);
@@ -66,10 +67,12 @@ public class DefaultSourceSet implements SourceSet {
         });
 
         String allSourceDisplayName = String.format("%s source", displayName);
-        allSource = new UnionFileTree(allSourceDisplayName, resources, javaSource);
+        allSource = new DefaultSourceDirectorySet(allSourceDisplayName, fileResolver);
+        allSource.source(resources);
+        allSource.source(javaSource);
 
         String classesDisplayName = String.format("%s classes", displayName);
-        classes = new PathResolvingFileCollection(classesDisplayName, fileResolver, taskResolver, new Callable() {
+        classes = new DefaultConfigurableFileCollection(classesDisplayName, fileResolver, taskResolver, new Callable() {
             public Object call() throws Exception {
                 return getClassesDir();
             }
@@ -161,7 +164,7 @@ public class DefaultSourceSet implements SourceSet {
         return this;
     }
 
-    public FileTree getAllJava() {
+    public SourceDirectorySet getAllJava() {
         return allJavaSource;
     }
 
@@ -174,7 +177,7 @@ public class DefaultSourceSet implements SourceSet {
         return this;
     }
 
-    public FileTree getAllSource() {
+    public SourceDirectorySet getAllSource() {
         return allSource;
     }
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/IncrementalJavaCompiler.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/IncrementalJavaCompiler.java
index 5d44ef1..72060f5 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/IncrementalJavaCompiler.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/IncrementalJavaCompiler.java
@@ -22,11 +22,11 @@ import org.gradle.api.internal.TaskOutputsInternal;
 import java.io.File;
 
 public class IncrementalJavaCompiler extends IncrementalJavaSourceCompiler<JavaCompiler> implements JavaCompiler {
-    private final Factory<? extends AntBuilder> antBuilderFactory;
+    private final Factory<AntBuilder> antBuilderFactory;
     private final TaskOutputsInternal taskOutputs;
     private File dependencyCacheDir;
 
-    public IncrementalJavaCompiler(JavaCompiler compiler, Factory<? extends AntBuilder> antBuilderFactory,
+    public IncrementalJavaCompiler(JavaCompiler compiler, Factory<AntBuilder> antBuilderFactory,
                                     TaskOutputsInternal taskOutputs) {
         super(compiler);
         this.antBuilderFactory = antBuilderFactory;
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/DefaultTestExecuter.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/DefaultTestExecuter.java
index 8fefed3..ff466d6 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/DefaultTestExecuter.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/DefaultTestExecuter.java
@@ -37,10 +37,10 @@ import org.gradle.util.TrueTimeProvider;
  * @author Tom Eyckmans
  */
 public class DefaultTestExecuter implements TestExecuter {
-    private final Factory<? extends WorkerProcessBuilder> workerFactory;
+    private final Factory<WorkerProcessBuilder> workerFactory;
     private final ActorFactory actorFactor;
 
-    public DefaultTestExecuter(Factory<? extends WorkerProcessBuilder> workerFactory, ActorFactory actorFactor) {
+    public DefaultTestExecuter(Factory<WorkerProcessBuilder> workerFactory, ActorFactory actorFactor) {
         this.workerFactory = workerFactory;
         this.actorFactor = actorFactor;
     }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/AntJUnitReport.groovy b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/AntJUnitReport.groovy
deleted file mode 100644
index d444421..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/AntJUnitReport.groovy
+++ /dev/null
@@ -1,34 +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.apache.tools.ant.taskdefs.optional.junit.XMLResultAggregator
-/**
- * @author Tom Eyckmans
- */
-
-class AntJUnitReport {
-
-    void execute(File testResultsDir, File testReportDir, AntBuilder ant) {
-        ant.project.addTaskDefinition('junitreport2', XMLResultAggregator.class)
-        ant.junitreport2(todir: testResultsDir.absolutePath) {
-            fileset(dir: testResultsDir.absolutePath, includes: 'TEST-*.xml')
-            report(todir: testReportDir.absolutePath)
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnit4TestResultProcessorAdapter.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnit4TestResultProcessorAdapter.java
deleted file mode 100644
index 2f469fe..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnit4TestResultProcessorAdapter.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.api.internal.tasks.testing.junit;
-
-import junit.framework.JUnit4TestCaseFacade;
-import junit.framework.Test;
-import org.gradle.api.internal.tasks.testing.DefaultTestDescriptor;
-import org.gradle.api.internal.tasks.testing.TestDescriptorInternal;
-import org.gradle.api.internal.tasks.testing.TestResultProcessor;
-import org.gradle.util.IdGenerator;
-import org.gradle.util.TimeProvider;
-import org.junit.runner.Describable;
-import org.junit.runner.Description;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class JUnit4TestResultProcessorAdapter extends JUnitTestResultProcessorAdapter {
-    public JUnit4TestResultProcessorAdapter(TestResultProcessor resultProcessor, TimeProvider timeProvider, IdGenerator<?> idGenerator) {
-        super(resultProcessor, timeProvider, idGenerator);
-    }
-
-    @Override
-    protected TestDescriptorInternal convert(Object id, Test test) {
-        if (test instanceof JUnit4TestCaseFacade) {
-            JUnit4TestCaseFacade facade = (JUnit4TestCaseFacade) test;
-            Matcher matcher = Pattern.compile("(.*)\\((.*)\\)").matcher(facade.toString());
-            String className = facade.toString();
-            String methodName = null;
-            if (matcher.matches()) {
-                className = matcher.group(2);
-                methodName = matcher.group(1);
-            }
-            return new DefaultTestDescriptor(id, className, methodName);
-        }
-        // JUnit4TestCaseFacade is-a Describable in junit 4.7+
-        if (test instanceof Describable) {
-            Describable describable = (Describable) test;
-            Description description = describable.getDescription();
-            return new DefaultTestDescriptor(id, description.getClassName(), description.getMethodName());
-        }
-        return super.convert(id, test);
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassExecuter.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassExecuter.java
index efe0225..7850e2f 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassExecuter.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassExecuter.java
@@ -16,24 +16,24 @@
 
 package org.gradle.api.internal.tasks.testing.junit;
 
-import junit.framework.*;
 import org.gradle.api.internal.tasks.testing.*;
 import org.gradle.util.IdGenerator;
 import org.gradle.util.TimeProvider;
-import org.junit.runner.Describable;
 import org.junit.runner.Description;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
+import org.junit.runner.Request;
+import org.junit.runner.Runner;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunListener;
+import org.junit.runner.notification.RunNotifier;
 
 public class JUnitTestClassExecuter {
     private final ClassLoader applicationClassLoader;
-    private final TestListener listener;
+    private final RunListener listener;
     private final TestResultProcessor resultProcessor;
     private final IdGenerator<?> idGenerator;
     private final TimeProvider timeProvider;
 
-    public JUnitTestClassExecuter(ClassLoader applicationClassLoader, TestListener listener, TestResultProcessor resultProcessor, IdGenerator<?> idGenerator, TimeProvider timeProvider) {
+    public JUnitTestClassExecuter(ClassLoader applicationClassLoader, RunListener listener, TestResultProcessor resultProcessor, IdGenerator<?> idGenerator, TimeProvider timeProvider) {
         this.applicationClassLoader = applicationClassLoader;
         this.listener = listener;
         this.resultProcessor = resultProcessor;
@@ -45,41 +45,24 @@ public class JUnitTestClassExecuter {
         TestDescriptorInternal testInternal = new DefaultTestClassDescriptor(idGenerator.generateId(), testClassName);
         resultProcessor.started(testInternal, new TestStartEvent(timeProvider.getCurrentTime()));
 
-        Test adapter = createTest(testClassName);
-        TestResult result = new TestResult();
-        result.addListener(listener);
-        adapter.run(result);
+        Runner runner = createTest(testClassName);
+        RunNotifier notifier = new RunNotifier();
+        notifier.addListener(listener);
+        runner.run(notifier);
 
         resultProcessor.completed(testInternal.getId(), new TestCompleteEvent(timeProvider.getCurrentTime()));
     }
 
-    private Test createTest(String testClassName) {
-        Class<?> testClass;
+    private Runner createTest(String testClassName) {
         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());
-            }
+            Class<?> testClass = Class.forName(testClassName, true, applicationClassLoader);
+            return Request.aClass(testClass).getRunner();
         } catch (Throwable e) {
-            return new BrokenTest(
-                    Description.createSuiteDescription(String.format("initializationError(%s)", testClassName)), e);
-        }
-
-        if (TestCase.class.isAssignableFrom(testClass)) {
-            // Use a TestSuite directly, so that we get access to the test object in JUnitTestResultProcessorAdapter
-            return new TestSuite(testClass.asSubclass(TestCase.class));
+            return new BrokenTest(Description.createSuiteDescription(String.format("initializationError(%s)", testClassName)), e);
         }
-        return new JUnit4TestAdapter(testClass);
     }
 
-    private static class BrokenTest implements Test, Describable {
+    private static class BrokenTest extends Runner {
         private final Throwable failure;
         private final Description description;
 
@@ -92,14 +75,11 @@ public class JUnitTestClassExecuter {
             return description;
         }
 
-        public int countTestCases() {
-            return 1;
-        }
-
-        public void run(TestResult result) {
-            result.startTest(this);
-            result.addError(this, failure);
-            result.endTest(this);
+        @Override
+        public void run(RunNotifier notifier) {
+            notifier.fireTestStarted(description);
+            notifier.fireTestFailure(new Failure(description, failure));
+            notifier.fireTestFinished(description);
         }
     }
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessor.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessor.java
index 28cfdf9..410b0e8 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessor.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessor.java
@@ -1,68 +1,68 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.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;
-import org.gradle.util.IdGenerator;
-import org.gradle.util.TimeProvider;
-import org.gradle.util.TrueTimeProvider;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-
-public class JUnitTestClassProcessor implements TestClassProcessor {
-    private static final Logger LOGGER = LoggerFactory.getLogger(JUnitTestClassProcessor.class);
-    private final File testResultsDir;
-    private final IdGenerator<?> idGenerator;
-    private final StandardOutputRedirector outputRedirector;
-    private final TimeProvider timeProvider = new TrueTimeProvider();
-    private JUnitTestClassExecuter executer;
-
-    public JUnitTestClassProcessor(File testResultsDir, IdGenerator<?> idGenerator,
-                                   StandardOutputRedirector standardOutputRedirector) {
-        this.testResultsDir = testResultsDir;
-        this.idGenerator = idGenerator;
-        this.outputRedirector = standardOutputRedirector;
-    }
-
-    public void startProcessing(TestResultProcessor resultProcessor) {
-        ClassLoader applicationClassLoader = Thread.currentThread().getContextClassLoader();
-        ListenerBroadcast<TestResultProcessor> processors = new ListenerBroadcast<TestResultProcessor>(
-                TestResultProcessor.class);
-        processors.add(new JUnitXmlReportGenerator(testResultsDir));
-        processors.add(resultProcessor);
-        TestResultProcessor resultProcessorChain = new AttachParentTestResultProcessor(new CaptureTestOutputTestResultProcessor(processors.getSource(), outputRedirector));
-        JUnitTestResultProcessorAdapter listener = new JUnit4TestResultProcessorAdapter(resultProcessorChain,
-                timeProvider, idGenerator);
-        executer = new JUnitTestClassExecuter(applicationClassLoader, listener, resultProcessorChain, idGenerator, timeProvider);
-    }
-
-    public void processTestClass(TestClassRunInfo testClass) {
-        LOGGER.debug("Executing test {}", testClass.getTestClassName());
-        executer.execute(testClass.getTestClassName());
-    }
-
-    public void stop() {
-    }
-}
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.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;
+import org.gradle.util.IdGenerator;
+import org.gradle.util.TimeProvider;
+import org.gradle.util.TrueTimeProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+
+public class JUnitTestClassProcessor implements TestClassProcessor {
+    private static final Logger LOGGER = LoggerFactory.getLogger(JUnitTestClassProcessor.class);
+    private final File testResultsDir;
+    private final IdGenerator<?> idGenerator;
+    private final StandardOutputRedirector outputRedirector;
+    private final TimeProvider timeProvider = new TrueTimeProvider();
+    private JUnitTestClassExecuter executer;
+
+    public JUnitTestClassProcessor(File testResultsDir, IdGenerator<?> idGenerator,
+                                   StandardOutputRedirector standardOutputRedirector) {
+        this.testResultsDir = testResultsDir;
+        this.idGenerator = idGenerator;
+        this.outputRedirector = standardOutputRedirector;
+    }
+
+    public void startProcessing(TestResultProcessor resultProcessor) {
+        ClassLoader applicationClassLoader = Thread.currentThread().getContextClassLoader();
+        ListenerBroadcast<TestResultProcessor> processors = new ListenerBroadcast<TestResultProcessor>(
+                TestResultProcessor.class);
+        processors.add(new JUnitXmlReportGenerator(testResultsDir));
+        processors.add(resultProcessor);
+        TestResultProcessor resultProcessorChain = new AttachParentTestResultProcessor(new CaptureTestOutputTestResultProcessor(processors.getSource(), outputRedirector));
+        JUnitTestResultProcessorAdapter listener = new JUnitTestResultProcessorAdapter(resultProcessorChain,
+                timeProvider, idGenerator);
+        executer = new JUnitTestClassExecuter(applicationClassLoader, listener, resultProcessorChain, idGenerator, timeProvider);
+    }
+
+    public void processTestClass(TestClassRunInfo testClass) {
+        LOGGER.debug("Executing test {}", testClass.getTestClassName());
+        executer.execute(testClass.getTestClassName());
+    }
+
+    public void stop() {
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestFramework.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestFramework.java
index cb8be74..95588f1 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestFramework.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestFramework.java
@@ -21,6 +21,8 @@ import org.gradle.api.internal.project.ServiceRegistry;
 import org.gradle.api.internal.tasks.testing.TestClassProcessor;
 import org.gradle.api.internal.tasks.testing.TestFramework;
 import org.gradle.api.internal.tasks.testing.WorkerTestClassProcessorFactory;
+import org.gradle.api.internal.tasks.testing.junit.report.DefaultTestReport;
+import org.gradle.api.internal.tasks.testing.junit.report.TestReporter;
 import org.gradle.api.tasks.testing.Test;
 import org.gradle.api.tasks.testing.junit.JUnitOptions;
 import org.gradle.process.internal.WorkerProcessBuilder;
@@ -33,14 +35,14 @@ import java.io.Serializable;
  * @author Tom Eyckmans
  */
 public class JUnitTestFramework implements TestFramework {
-    private AntJUnitReport antJUnitReport;
+    private TestReporter reporter;
     private JUnitOptions options;
     private JUnitDetector detector;
     private final Test testTask;
 
     public JUnitTestFramework(Test testTask) {
         this.testTask = testTask;
-        antJUnitReport = new AntJUnitReport();
+        reporter = new DefaultTestReport();
         options = new JUnitOptions();
         detector = new JUnitDetector(testTask.getTestClassesDir(), testTask.getClasspath());
     }
@@ -64,7 +66,9 @@ public class JUnitTestFramework implements TestFramework {
         if (!testTask.isTestReport()) {
             return;
         }
-        antJUnitReport.execute(testTask.getTestResultsDir(), testTask.getTestReportDir(), testTask.getProject().getAnt());
+        reporter.setTestReportDir(testTask.getTestReportDir());
+        reporter.setTestResultsDir(testTask.getTestResultsDir());
+        reporter.generateReport();
     }
 
     public JUnitOptions getOptions() {
@@ -75,12 +79,12 @@ public class JUnitTestFramework implements TestFramework {
         this.options = options;
     }
 
-    AntJUnitReport getAntJUnitReport() {
-        return antJUnitReport;
+    TestReporter getReporter() {
+        return reporter;
     }
 
-    void setAntJUnitReport(AntJUnitReport antJUnitReport) {
-        this.antJUnitReport = antJUnitReport;
+    void setReporter(TestReporter reporter) {
+        this.reporter = reporter;
     }
 
     public JUnitDetector getDetector() {
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestResultProcessorAdapter.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestResultProcessorAdapter.java
index c3af152..3bd5596 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestResultProcessorAdapter.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestResultProcessorAdapter.java
@@ -1,107 +1,138 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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 junit.extensions.TestSetup;
-import junit.framework.*;
-import org.gradle.api.internal.tasks.testing.*;
-import org.gradle.util.IdGenerator;
-import org.gradle.util.TimeProvider;
-
-import java.util.IdentityHashMap;
-import java.util.Map;
-
-public class JUnitTestResultProcessorAdapter implements TestListener {
-    private final TestResultProcessor resultProcessor;
-    private final TimeProvider timeProvider;
-    private final IdGenerator<?> idGenerator;
-    private final Object lock = new Object();
-    private final Map<Object, TestDescriptorInternal> executing = new IdentityHashMap<Object, TestDescriptorInternal>();
-
-    public JUnitTestResultProcessorAdapter(TestResultProcessor resultProcessor, TimeProvider timeProvider,
-                                 IdGenerator<?> idGenerator) {
-        this.resultProcessor = resultProcessor;
-        this.timeProvider = timeProvider;
-        this.idGenerator = idGenerator;
-    }
-
-    public void startTest(Test test) {
-        TestDescriptorInternal descriptor = convert(idGenerator.generateId(), test);
-        doStartTest(test, descriptor);
-    }
-
-    private void doStartTest(Test test, TestDescriptorInternal descriptor) {
-        synchronized (lock) {
-            TestDescriptorInternal oldTest = executing.put(test, descriptor);
-            assert oldTest == null : String.format("Unexpected start event for test '%s' (class %s)", test, test.getClass());
-        }
-        long startTime = timeProvider.getCurrentTime();
-        resultProcessor.started(descriptor, new TestStartEvent(startTime));
-    }
-
-    public void addError(Test test, Throwable throwable) {
-        TestDescriptorInternal testInternal;
-        synchronized (lock) {
-            testInternal = executing.get(test);
-        }
-        boolean needEndEvent = false;
-        if (testInternal == null) {
-            // this happens when @AfterClass fails, for example. Synthesise a start and end events
-            testInternal = convertForError(test);
-            needEndEvent = true;
-            doStartTest(test, testInternal);
-        }
-        resultProcessor.failure(testInternal.getId(), throwable);
-
-        if (needEndEvent) {
-            endTest(test);
-        }
-    }
-
-    private TestDescriptorInternal convertForError(Test test) {
-        if (test instanceof TestSetup) {
-            TestSetup testSetup = (TestSetup) test;
-            return new DefaultTestDescriptor(idGenerator.generateId(), testSetup.getClass().getName(), "classMethod");
-        }
-        assert test instanceof TestSuite : String.format("Unexpected type for test '%s'. Should be TestSuite, is %s", test, test.getClass());
-        TestSuite suite = (TestSuite) test;
-        return new DefaultTestMethodDescriptor(idGenerator.generateId(), suite.getName(), "classMethod");
-    }
-
-    public void addFailure(Test test, AssertionFailedError assertionFailedError) {
-        addError(test, assertionFailedError);
-    }
-
-    public void endTest(Test test) {
-        long endTime = timeProvider.getCurrentTime();
-        TestDescriptorInternal testInternal;
-        synchronized (lock) {
-            testInternal = executing.remove(test);
-            assert testInternal != null : String.format("Unexpected end event for test '%s' (class %s)", test, test.getClass());
-        }
-        resultProcessor.completed(testInternal.getId(), new TestCompleteEvent(endTime));
-    }
-
-    protected TestDescriptorInternal convert(Object id, Test test) {
-        if (test instanceof TestCase) {
-            TestCase testCase = (TestCase) test;
-            return new DefaultTestDescriptor(id, testCase.getClass().getName(), testCase.getName());
-        }
-        return new DefaultTestDescriptor(id, test.getClass().getName(), test.toString());
-    }
-}
-
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.tasks.testing.TestResult;
+import org.gradle.util.IdGenerator;
+import org.gradle.util.TimeProvider;
+import org.junit.runner.Description;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunListener;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class JUnitTestResultProcessorAdapter extends RunListener {
+    private final TestResultProcessor resultProcessor;
+    private final TimeProvider timeProvider;
+    private final IdGenerator<?> idGenerator;
+    private final Object lock = new Object();
+    private final Map<Description, TestDescriptorInternal> executing = new HashMap<Description, TestDescriptorInternal>();
+
+    public JUnitTestResultProcessorAdapter(TestResultProcessor resultProcessor, TimeProvider timeProvider,
+                                           IdGenerator<?> idGenerator) {
+        this.resultProcessor = resultProcessor;
+        this.timeProvider = timeProvider;
+        this.idGenerator = idGenerator;
+    }
+
+    @Override
+    public void testStarted(Description description) throws Exception {
+        TestDescriptorInternal descriptor = descriptor(idGenerator.generateId(), description);
+        synchronized (lock) {
+            TestDescriptorInternal oldTest = executing.put(description, descriptor);
+            assert oldTest == null : String.format("Unexpected start event for %s", description);
+        }
+        resultProcessor.started(descriptor, startEvent());
+    }
+
+    @Override
+    public void testFailure(Failure failure) throws Exception {
+        TestDescriptorInternal testInternal;
+        synchronized (lock) {
+            testInternal = executing.get(failure.getDescription());
+        }
+        boolean needEndEvent = false;
+        if (testInternal == null) {
+            // This can happen when, for example, a @BeforeClass or @AfterClass method fails
+            needEndEvent = true;
+            testInternal = failureDescriptor(idGenerator.generateId(), failure.getDescription());
+            resultProcessor.started(testInternal, startEvent());
+        }
+        resultProcessor.failure(testInternal.getId(), failure.getException());
+        if (needEndEvent) {
+            resultProcessor.completed(testInternal.getId(), new TestCompleteEvent(timeProvider.getCurrentTime()));
+        }
+    }
+
+    @Override
+    public void testIgnored(Description description) throws Exception {
+        if (methodName(description) == null) {
+            // An @Ignored class, ignore the event. We don't get testIgnored events for each method, which would be kind of nice
+            return;
+        }
+        TestDescriptorInternal testInternal = descriptor(idGenerator.generateId(), description);
+        resultProcessor.started(testInternal, startEvent());
+        resultProcessor.completed(testInternal.getId(), new TestCompleteEvent(timeProvider.getCurrentTime(), TestResult.ResultType.SKIPPED));
+    }
+
+    @Override
+    public void testFinished(Description description) throws Exception {
+        long endTime = timeProvider.getCurrentTime();
+        TestDescriptorInternal testInternal;
+        synchronized (lock) {
+            testInternal = executing.remove(description);
+            if (testInternal == null && executing.size() == 1) {
+                // Assume that test has renamed itself
+                testInternal = executing.values().iterator().next();
+                executing.clear();
+            }
+            assert testInternal != null : String.format("Unexpected end event for %s", description);
+        }
+        resultProcessor.completed(testInternal.getId(), new TestCompleteEvent(endTime));
+    }
+
+    private TestStartEvent startEvent() {
+        return new TestStartEvent(timeProvider.getCurrentTime());
+    }
+
+    private TestDescriptorInternal descriptor(Object id, Description description) {
+        return new DefaultTestDescriptor(id, className(description), methodName(description));
+    }
+
+    private TestDescriptorInternal failureDescriptor(Object id, Description description) {
+        if (methodName(description) != null) {
+            return new DefaultTestDescriptor(id, className(description), methodName(description));
+        } else {
+            return new DefaultTestDescriptor(id, className(description), "classMethod");
+        }
+    }
+
+    // Use this instead of Description.getMethodName(), it is not available in JUnit <= 4.5
+    private String methodName(Description description) {
+        Matcher matcher = methodStringMatcher(description);
+        if (matcher.matches()) {
+            return matcher.group(1);
+        }
+        return null;
+    }
+
+    // Use this instead of Description.getClassName(), it is not available in JUnit <= 4.5
+    private String className(Description description) {
+        Matcher matcher = methodStringMatcher(description);
+        return matcher.matches() ? matcher.group(2) : description.toString();
+    }
+
+    private Matcher methodStringMatcher(Description description) {
+        return Pattern.compile("(.*)\\((.*)\\)").matcher(description.toString());
+    }
+
+}
+
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitXmlReportGenerator.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitXmlReportGenerator.java
index 4e0ae23..da5f014 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitXmlReportGenerator.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitXmlReportGenerator.java
@@ -16,13 +16,13 @@
 
 package org.gradle.api.internal.tasks.testing.junit;
 
-import org.apache.tools.ant.taskdefs.optional.junit.XMLConstants;
 import org.apache.tools.ant.util.DOMElementWriter;
 import org.apache.tools.ant.util.DateUtils;
 import org.gradle.api.GradleException;
 import org.gradle.api.internal.tasks.testing.TestDescriptorInternal;
 import org.gradle.api.internal.tasks.testing.TestOutputEvent;
 import org.gradle.api.internal.tasks.testing.results.StateTrackingTestResultProcessor;
+import org.gradle.api.tasks.testing.TestResult;
 import org.gradle.util.UncheckedException;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
@@ -65,10 +65,10 @@ public class JUnitXmlReportGenerator extends StateTrackingTestResultProcessor {
         TestDescriptorInternal test = state.test;
         if (test.getName().equals(test.getClassName())) {
             testSuiteReport = documentBuilder.newDocument();
-            rootElement = testSuiteReport.createElement(XMLConstants.TESTSUITE);
+            rootElement = testSuiteReport.createElement("testsuite");
             testSuiteReport.appendChild(rootElement);
             // Add an empty properties element for compatibility
-            rootElement.appendChild(testSuiteReport.createElement(XMLConstants.PROPERTIES));
+            rootElement.appendChild(testSuiteReport.createElement("properties"));
             outputs.put(TestOutputEvent.Destination.StdOut, new StringBuilder());
             outputs.put(TestOutputEvent.Destination.StdErr, new StringBuilder());
             testSuite = state;
@@ -80,35 +80,34 @@ public class JUnitXmlReportGenerator extends StateTrackingTestResultProcessor {
         String testClassName = state.test.getClassName();
         Element element;
         if (!state.equals(testSuite)) {
-            element = testSuiteReport.createElement(XMLConstants.TESTCASE);
-            element.setAttribute(XMLConstants.ATTR_NAME, state.test.getName());
-            element.setAttribute(XMLConstants.ATTR_CLASSNAME, testClassName);
+            element = testSuiteReport.createElement(state.resultType == TestResult.ResultType.SKIPPED ? "ignored-testcase" : "testcase");
+            element.setAttribute("name", state.test.getName());
+            element.setAttribute("classname", testClassName);
             rootElement.appendChild(element);
         } else {
             element = rootElement;
-            rootElement.setAttribute(XMLConstants.ATTR_NAME, testClassName);
-            rootElement.setAttribute(XMLConstants.ATTR_TESTS, String.valueOf(state.testCount));
-            rootElement.setAttribute(XMLConstants.ATTR_FAILURES, String.valueOf(state.failedCount));
-            rootElement.setAttribute(XMLConstants.ATTR_ERRORS, "0");
-            rootElement.setAttribute(XMLConstants.TIMESTAMP, DateUtils.format(state.getStartTime(),
-                    DateUtils.ISO8601_DATETIME_PATTERN));
-            rootElement.setAttribute(XMLConstants.HOSTNAME, hostName);
-            Element stdoutElement = testSuiteReport.createElement(XMLConstants.SYSTEM_OUT);
+            rootElement.setAttribute("name", testClassName);
+            rootElement.setAttribute("tests", String.valueOf(state.testCount));
+            rootElement.setAttribute("failures", String.valueOf(state.failedCount));
+            rootElement.setAttribute("errors", "0");
+            rootElement.setAttribute("timestamp", DateUtils.format(state.getStartTime(), DateUtils.ISO8601_DATETIME_PATTERN));
+            rootElement.setAttribute("hostname", hostName);
+            Element stdoutElement = testSuiteReport.createElement("system-out");
             stdoutElement.appendChild(testSuiteReport.createCDATASection(outputs.get(TestOutputEvent.Destination.StdOut)
                     .toString()));
             rootElement.appendChild(stdoutElement);
-            Element stderrElement = testSuiteReport.createElement(XMLConstants.SYSTEM_ERR);
+            Element stderrElement = testSuiteReport.createElement("system-err");
             stderrElement.appendChild(testSuiteReport.createCDATASection(outputs.get(TestOutputEvent.Destination.StdErr)
                     .toString()));
             rootElement.appendChild(stderrElement);
         }
 
-        element.setAttribute(XMLConstants.ATTR_TIME, String.valueOf(state.getExecutionTime() / 1000.0));
+        element.setAttribute("time", String.valueOf(state.getExecutionTime() / 1000.0));
         for (Throwable failure : state.failures) {
-            Element failureElement = testSuiteReport.createElement(XMLConstants.FAILURE);
+            Element failureElement = testSuiteReport.createElement("failure");
             element.appendChild(failureElement);
-            failureElement.setAttribute(XMLConstants.ATTR_MESSAGE, failureMessage(failure));
-            failureElement.setAttribute(XMLConstants.ATTR_TYPE, failure.getClass().getName());
+            failureElement.setAttribute("message", failureMessage(failure));
+            failureElement.setAttribute("type", failure.getClass().getName());
             failureElement.appendChild(testSuiteReport.createTextNode(stackTrace(failure)));
         }
 
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/AllTestResults.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/AllTestResults.java
new file mode 100644
index 0000000..dc585ac
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/AllTestResults.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report;
+
+import org.apache.commons.lang.StringUtils;
+
+import java.util.*;
+
+/**
+ * The model for the test report.
+ */
+public class AllTestResults extends CompositeTestResults {
+    private final Map<String, PackageTestResults> packages = new TreeMap<String, PackageTestResults>();
+
+    public AllTestResults() {
+        super(null);
+    }
+
+    @Override
+    public String getTitle() {
+        return "Test Summary";
+    }
+
+    public Collection<PackageTestResults> getPackages() {
+        return packages.values();
+    }
+
+    public TestResult addTest(String className, String testName, long duration) {
+        PackageTestResults packageResults = addPackageForClass(className);
+        return addTest(packageResults.addTest(className, testName, duration));
+    }
+
+    public ClassTestResults addTestClass(String className) {
+        return addPackageForClass(className).addClass(className);
+    }
+
+    private PackageTestResults addPackageForClass(String className) {
+        String packageName = StringUtils.substringBeforeLast(className, ".");
+        if (packageName.equals(className)) {
+            packageName = "";
+        }
+        return addPackage(packageName);
+    }
+
+    private PackageTestResults addPackage(String packageName) {
+        PackageTestResults packageResults = packages.get(packageName);
+        if (packageResults == null) {
+            packageResults = new PackageTestResults(packageName, this);
+            packages.put(packageName, packageResults);
+        }
+        return packageResults;
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/ClassPageRenderer.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/ClassPageRenderer.java
new file mode 100644
index 0000000..a053524
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/ClassPageRenderer.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report;
+
+import org.gradle.api.Action;
+import org.w3c.dom.Element;
+
+class ClassPageRenderer extends PageRenderer<ClassTestResults> {
+
+    @Override protected void renderBreadcrumbs(Element parent) {
+        Element div = append(parent, "div");
+        div.setAttribute("class", "breadcrumbs");
+        appendLink(div, "index.html", "all");
+        appendText(div, " > ");
+        appendLink(div, String.format("%s.html", getResults().getPackageResults().getName()), getResults().getPackageResults().getName());
+        appendText(div, String.format(" > %s", getResults().getSimpleName()));
+    }
+
+    private void renderTests(Element parent) {
+        Element table = append(parent, "table");
+        Element thead = append(table, "thead");
+        Element tr = append(thead, "tr");
+        appendWithText(tr, "th", "Test");
+        appendWithText(tr, "th", "Duration");
+        appendWithText(tr, "th", "Result");
+        for (TestResult test : getResults().getTestResults()) {
+            tr = append(table, "tr");
+            Element td = appendWithText(tr, "td", test.getName());
+            td.setAttribute("class", test.getStatusClass());
+            appendWithText(td, "td", test.getFormattedDuration());
+            td = appendWithText(td, "td", test.getFormattedResultType());
+            td.setAttribute("class", test.getStatusClass());
+        }
+    }
+
+    @Override protected void renderFailures(Element parent) {
+        for (TestResult test : getResults().getFailures()) {
+            Element div = append(parent, "div");
+            div.setAttribute("class", "test");
+            append(div, "a").setAttribute("name", test.getId().toString());
+            appendWithText(div, "h3", test.getName()).setAttribute("class", test.getStatusClass());
+            for (TestFailure failure : test.getFailures()) {
+                appendWithText(div, "pre", failure.getStackTrace()).setAttribute("class", "stackTrace");
+            }
+        }
+    }
+
+    private void renderStdOut(Element parent) {
+        appendWithText(parent, "pre", getResults().getStandardOutput());
+    }
+
+    private void renderStdErr(Element parent) {
+        appendWithText(parent, "pre", getResults().getStandardError());
+    }
+
+    @Override protected void registerTabs() {
+        addFailuresTab();
+        addTab("Tests", new Action<Element>() {
+            public void execute(Element element) {
+                renderTests(element);
+            }
+        });
+        if (getResults().getStandardOutput().length() > 0) {
+            addTab("Standard output", new Action<Element>() {
+                public void execute(Element element) {
+                    renderStdOut(element);
+                }
+            });
+        }
+        if (getResults().getStandardError().length() > 0) {
+            addTab("Standard error", new Action<Element>() {
+                public void execute(Element element) {
+                    renderStdErr(element);
+                }
+            });
+        }
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/ClassTestResults.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/ClassTestResults.java
new file mode 100644
index 0000000..a91aebf
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/ClassTestResults.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report;
+
+import org.apache.commons.lang.StringUtils;
+
+import java.util.Collection;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Test results for a given class.
+ */
+public class ClassTestResults extends CompositeTestResults {
+    private final String name;
+    private final PackageTestResults packageResults;
+    private final Set<TestResult> results = new TreeSet<TestResult>();
+    private final StringBuilder standardOutput = new StringBuilder();
+    private final StringBuilder standardError = new StringBuilder();
+
+    public ClassTestResults(String name, PackageTestResults packageResults) {
+        super(packageResults);
+        this.name = name;
+        this.packageResults = packageResults;
+    }
+
+    @Override
+    public String getTitle() {
+        return String.format("Class %s", name);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getSimpleName() {
+        String simpleName = StringUtils.substringAfterLast(name, ".");
+        if (simpleName.equals("")) {
+            return name;
+        }
+        return simpleName;
+    }
+
+    public PackageTestResults getPackageResults() {
+        return packageResults;
+    }
+
+    public Collection<TestResult> getTestResults() {
+        return results;
+    }
+
+    public CharSequence getStandardError() {
+        return standardError;
+    }
+
+    public CharSequence getStandardOutput() {
+        return standardOutput;
+    }
+
+    public TestResult addTest(String testName, long duration) {
+        TestResult test = new TestResult(testName, duration, this);
+        results.add(test);
+        return addTest(test);
+    }
+
+    public void addStandardOutput(String textContent) {
+        standardOutput.append(textContent);
+    }
+
+    public void addStandardError(String textContent) {
+        standardError.append(textContent);
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/CompositeTestResults.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/CompositeTestResults.java
new file mode 100644
index 0000000..6bc0a34
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/CompositeTestResults.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report;
+
+import java.math.BigDecimal;
+import java.util.Set;
+import java.util.TreeSet;
+
+import static org.gradle.api.tasks.testing.TestResult.ResultType;
+
+public abstract class CompositeTestResults extends TestResultModel {
+    private final CompositeTestResults parent;
+    private int tests;
+    private final Set<TestResult> failures = new TreeSet<TestResult>();
+    private long duration;
+
+    protected CompositeTestResults(CompositeTestResults parent) {
+        this.parent = parent;
+    }
+
+    public int getTestCount() {
+        return tests;
+    }
+
+    public int getFailureCount() {
+        return failures.size();
+    }
+
+    public long getDuration() {
+        return duration;
+    }
+
+    @Override
+    public String getFormattedDuration() {
+        return getTestCount() == 0 ? "-" : super.getFormattedDuration();
+    }
+
+    public Set<TestResult> getFailures() {
+        return failures;
+    }
+
+    public ResultType getResultType() {
+        return failures.isEmpty() ? ResultType.SUCCESS : ResultType.FAILURE;
+    }
+
+    public String getFormattedSuccessRate() {
+        Number successRate = getSuccessRate();
+        if (successRate == null) {
+            return "-";
+        }
+        return successRate + "%";
+    }
+
+    public Number getSuccessRate() {
+        if (getTestCount() == 0) {
+            return null;
+        }
+
+        BigDecimal tests = BigDecimal.valueOf(getTestCount());
+        BigDecimal successful = BigDecimal.valueOf(getTestCount() - getFailureCount());
+
+        return successful.divide(tests, 2, BigDecimal.ROUND_DOWN).multiply(BigDecimal.valueOf(100)).intValue();
+    }
+
+    protected void failed(TestResult failedTest) {
+        failures.add(failedTest);
+        if (parent != null) {
+            parent.failed(failedTest);
+        }
+    }
+
+    protected TestResult addTest(TestResult test) {
+        tests++;
+        duration += test.getDuration();
+        return test;
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/DefaultTestReport.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/DefaultTestReport.java
new file mode 100644
index 0000000..7e915df
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/DefaultTestReport.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report;
+
+import org.apache.commons.io.IOUtils;
+import org.gradle.api.GradleException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import java.io.*;
+import java.math.BigDecimal;
+
+public class DefaultTestReport implements TestReporter {
+    private File resultDir;
+    private File reportDir;
+    private DocumentBuilder documentBuilder;
+    private Transformer transformer;
+
+    public void setTestResultsDir(File resultDir) {
+        this.resultDir = resultDir;
+    }
+
+    public void setTestReportDir(File reportDir) {
+        this.reportDir = reportDir;
+    }
+
+    public void generateReport() {
+        AllTestResults model = loadModel();
+        generateFiles(model);
+    }
+
+    private AllTestResults loadModel() {
+        AllTestResults model = new AllTestResults();
+        if (resultDir.exists()) {
+            for (File file : resultDir.listFiles()) {
+                if (file.getName().startsWith("TEST-") && file.getName().endsWith(".xml")) {
+                    mergeFromFile(file, model);
+                }
+            }
+        }
+        return model;
+    }
+
+    private void mergeFromFile(File file, AllTestResults model) {
+        try {
+            InputStream inputStream = new FileInputStream(file);
+            Document document;
+            try {
+                document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(inputStream));
+            } finally {
+                inputStream.close();
+            }
+            NodeList testCases = document.getElementsByTagName("testcase");
+            for (int i = 0; i < testCases.getLength(); i++) {
+                Element testCase = (Element) testCases.item(i);
+                String className = testCase.getAttribute("classname");
+                String testName = testCase.getAttribute("name");
+                LocaleSafeDecimalFormat format = new LocaleSafeDecimalFormat();
+                BigDecimal duration = format.parse(testCase.getAttribute("time"));
+                duration = duration.multiply(BigDecimal.valueOf(1000));
+                NodeList failures = testCase.getElementsByTagName("failure");
+                TestResult testResult = model.addTest(className, testName, duration.longValue());
+                for (int j = 0; j < failures.getLength(); j++) {
+                    Element failure = (Element) failures.item(j);
+                    testResult.addFailure(failure.getAttribute("message"), failure.getTextContent());
+                }
+            }
+            NodeList ignoredTestCases = document.getElementsByTagName("ignored-testcase");
+            for (int i = 0; i < ignoredTestCases.getLength(); i++) {
+                Element testCase = (Element) ignoredTestCases.item(i);
+                String className = testCase.getAttribute("classname");
+                String testName = testCase.getAttribute("name");
+                model.addTest(className, testName, 0).ignored();
+            }
+            String suiteClassName = document.getDocumentElement().getAttribute("name");
+            ClassTestResults suiteResults = model.addTestClass(suiteClassName);
+            NodeList stdOutElements = document.getElementsByTagName("system-out");
+            for (int i = 0; i < stdOutElements.getLength(); i++) {
+                suiteResults.addStandardOutput(stdOutElements.item(i).getTextContent());
+            }
+            NodeList stdErrElements = document.getElementsByTagName("system-err");
+            for (int i = 0; i < stdErrElements.getLength(); i++) {
+                suiteResults.addStandardError(stdErrElements.item(i).getTextContent());
+            }
+        } catch (Exception e) {
+            throw new GradleException(String.format("Could not load test results from '%s'.", file), e);
+        }
+    }
+
+    private void generateFiles(AllTestResults model) {
+        try {
+            generatePage(model, new OverviewPageRenderer(), new File(reportDir, "index.html"));
+            for (PackageTestResults packageResults : model.getPackages()) {
+                generatePage(packageResults, new PackagePageRenderer(), new File(reportDir, packageResults.getName() + ".html"));
+                for (ClassTestResults classResults : packageResults.getClasses()) {
+                    generatePage(classResults, new ClassPageRenderer(), new File(reportDir, classResults.getName() + ".html"));
+                }
+            }
+
+            copyResources();
+
+        } catch (Exception e) {
+            throw new GradleException(String.format("Could not generate test report to '%s'.", reportDir), e);
+        }
+    }
+
+    private <T extends CompositeTestResults> void generatePage(T model, PageRenderer<T> renderer, File outputFile) throws Exception {
+        if (documentBuilder == null) {
+            documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+        }
+        Document document = documentBuilder.newDocument();
+        renderer.render(document, model);
+
+        if (transformer == null) {
+            TransformerFactory factory = TransformerFactory.newInstance();
+            transformer = factory.newTransformer();
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+            transformer.setOutputProperty(OutputKeys.METHOD, "html");
+            transformer.setOutputProperty(OutputKeys.MEDIA_TYPE, "text/html");
+        }
+
+        outputFile.getParentFile().mkdirs();
+        Writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile), "utf-8"));
+        try {
+            writer.write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n");
+            transformer.transform(new DOMSource(document), new StreamResult(writer));
+        } finally {
+            writer.close();
+        }
+    }
+
+    private void copyResources() throws IOException {
+        copyResource("style.css");
+        copyResource("report.js");
+        copyResource("css3-pie-1.0beta3.htc");
+    }
+
+    private void copyResource(String resourceName) throws IOException {
+        File cssFile = new File(reportDir, resourceName);
+        OutputStream outputStream = new FileOutputStream(cssFile);
+        try {
+            InputStream cssResource = getClass().getResourceAsStream(resourceName);
+            try {
+                IOUtils.copy(cssResource, outputStream);
+            } finally {
+                cssResource.close();
+            }
+        } finally {
+            outputStream.close();
+        }
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/LocaleSafeDecimalFormat.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/LocaleSafeDecimalFormat.java
new file mode 100644
index 0000000..c398f33
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/LocaleSafeDecimalFormat.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report;
+
+import java.math.BigDecimal;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.ParseException;
+
+/**
+ * @author Szczepan Faber, @date: 09.03.11
+ */
+public class LocaleSafeDecimalFormat {
+
+    /**
+     * Regardless of the default locale, comma ('.') is used as decimal separator
+     *
+     * @param source
+     * @return
+     * @throws ParseException
+     */
+    public BigDecimal parse(String source) throws ParseException {
+        DecimalFormatSymbols symbols = new DecimalFormatSymbols();
+        symbols.setDecimalSeparator('.');
+        DecimalFormat format = new DecimalFormat("#.#", symbols);
+        format.setParseBigDecimal(true);
+        return (BigDecimal) format.parse(source);
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/OverviewPageRenderer.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/OverviewPageRenderer.java
new file mode 100644
index 0000000..8d653a1
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/OverviewPageRenderer.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report;
+
+import org.gradle.api.Action;
+import org.w3c.dom.Element;
+
+class OverviewPageRenderer extends PageRenderer<AllTestResults> {
+
+    @Override protected void registerTabs() {
+        addFailuresTab();
+        if (!getResults().getPackages().isEmpty()) {
+            addTab("Packages", new Action<Element>() {
+                public void execute(Element element) {
+                    renderPackages(element);
+                }
+            });
+        }
+        addTab("Classes", new Action<Element>() {
+            public void execute(Element element) {
+                renderClasses(element);
+            }
+        });
+    }
+
+    @Override protected void renderBreadcrumbs(Element element) {
+    }
+
+    private void renderPackages(Element parent) {
+        Element table = append(parent, "table");
+        Element thead = append(table, "thead");
+        Element tr = append(thead, "tr");
+        appendWithText(tr, "th", "Package");
+        appendWithText(tr, "th", "Tests");
+        appendWithText(tr, "th", "Failures");
+        appendWithText(tr, "th", "Duration");
+        appendWithText(tr, "th", "Success rate");
+        for (PackageTestResults testPackage : getResults().getPackages()) {
+            tr = append(table, "tr");
+            Element td = append(tr, "td");
+            td.setAttribute("class", testPackage.getStatusClass());
+            appendLink(td, String.format("%s.html", testPackage.getName()), testPackage.getName());
+            appendWithText(td, "td", testPackage.getTestCount());
+            appendWithText(td, "td", testPackage.getFailureCount());
+            appendWithText(td, "td", testPackage.getFormattedDuration());
+            td = appendWithText(td, "td", testPackage.getFormattedSuccessRate());
+            td.setAttribute("class", testPackage.getStatusClass());
+        }
+    }
+
+    private void renderClasses(Element parent) {
+        Element table = append(parent, "table");
+        Element thead = append(table, "thead");
+        Element tr = append(thead, "tr");
+        appendWithText(tr, "th", "Class");
+        appendWithText(tr, "th", "Tests");
+        appendWithText(tr, "th", "Failures");
+        appendWithText(tr, "th", "Duration");
+        appendWithText(tr, "th", "Success rate");
+        for (PackageTestResults testPackage : getResults().getPackages()) {
+            for (ClassTestResults testClass : testPackage.getClasses()) {
+                tr = append(table, "tr");
+                Element td = append(tr, "td");
+                td.setAttribute("class", testClass.getStatusClass());
+                appendLink(td, String.format("%s.html", testClass.getName()), testClass.getName());
+                appendWithText(td, "td", testClass.getTestCount());
+                appendWithText(td, "td", testClass.getFailureCount());
+                appendWithText(td, "td", testClass.getFormattedDuration());
+                td = appendWithText(td, "td", testClass.getFormattedSuccessRate());
+                td.setAttribute("class", testClass.getStatusClass());
+            }
+        }
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/PackagePageRenderer.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/PackagePageRenderer.java
new file mode 100644
index 0000000..5205330
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/PackagePageRenderer.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report;
+
+import org.gradle.api.Action;
+import org.w3c.dom.Element;
+
+class PackagePageRenderer extends PageRenderer<PackageTestResults> {
+
+    @Override protected void renderBreadcrumbs(Element parent) {
+        Element div = append(parent, "div");
+        div.setAttribute("class", "breadcrumbs");
+        appendLink(div, "index.html", "all");
+        appendText(div, String.format(" > %s", getResults().getName()));
+    }
+
+    private void renderClasses(Element parent) {
+        Element table = append(parent, "table");
+        Element thead = append(table, "thead");
+        Element tr = append(thead, "tr");
+        appendWithText(tr, "th", "Class");
+        appendWithText(tr, "th", "Tests");
+        appendWithText(tr, "th", "Failures");
+        appendWithText(tr, "th", "Duration");
+        appendWithText(tr, "th", "Success rate");
+        for (ClassTestResults testClass : getResults().getClasses()) {
+            tr = append(table, "tr");
+            Element td = append(tr, "td");
+            td.setAttribute("class", testClass.getStatusClass());
+            appendLink(td, String.format("%s.html", testClass.getName()), testClass.getSimpleName());
+            appendWithText(td, "td", testClass.getTestCount());
+            appendWithText(td, "td", testClass.getFailureCount());
+            appendWithText(td, "td", testClass.getFormattedDuration());
+            td = appendWithText(td, "td", testClass.getFormattedSuccessRate());
+            td.setAttribute("class", testClass.getStatusClass());
+        }
+    }
+
+    @Override protected void registerTabs() {
+        addFailuresTab();
+        addTab("Classes", new Action<Element>() {
+            public void execute(Element element) {
+                renderClasses(element);
+            }
+        });
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/PackageTestResults.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/PackageTestResults.java
new file mode 100644
index 0000000..916397b
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/PackageTestResults.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * The test results for a given package.
+ */
+public class PackageTestResults extends CompositeTestResults {
+    private static final String DEFAULT_PACKAGE = "default-package";
+    private final String name;
+    private final Map<String, ClassTestResults> classes = new TreeMap<String, ClassTestResults>();
+
+    public PackageTestResults(String name, AllTestResults model) {
+        super(model);
+        this.name = name.length() == 0 ? DEFAULT_PACKAGE : name;
+    }
+
+    @Override
+    public String getTitle() {
+        return name.equals(DEFAULT_PACKAGE) ? "Default package" : String.format("Package %s", name);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Collection<ClassTestResults> getClasses() {
+        return classes.values();
+    }
+
+    public TestResult addTest(String className, String testName, long duration) {
+        ClassTestResults classResults = addClass(className);
+        return addTest(classResults.addTest(testName, duration));
+    }
+
+    public ClassTestResults addClass(String className) {
+        ClassTestResults classResults = classes.get(className);
+        if (classResults == null) {
+            classResults = new ClassTestResults(className, this);
+            classes.put(className, classResults);
+        }
+        return classResults;
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/PageRenderer.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/PageRenderer.java
new file mode 100644
index 0000000..7fa7d51
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/PageRenderer.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report;
+
+import org.gradle.api.Action;
+import org.gradle.util.GradleVersion;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+abstract class PageRenderer<T extends CompositeTestResults> {
+    private T results;
+    private List<TabDefinition> tabs = new ArrayList<TabDefinition>();
+
+    protected T getResults() {
+        return results;
+    }
+
+    protected abstract void renderBreadcrumbs(Element parent);
+
+    protected abstract void registerTabs();
+
+    protected void addTab(String title, Action<Element> contentRenderer) {
+        tabs.add(new TabDefinition(title, contentRenderer));
+    }
+
+    protected void renderTabs(Element element) {
+        Element tabs = appendWithId(element, "div", "tabs");
+        Element ul = append(tabs, "ul");
+        ul.setAttribute("class", "tabLinks");
+        for (int i = 0; i < this.tabs.size(); i++) {
+            TabDefinition tab = this.tabs.get(i);
+            Element li = append(ul, "li");
+            Element a = appendWithText(li, "a", tab.title);
+            String tabId = String.format("tab%s", i);
+            a.setAttribute("href", "#" + tabId);
+            Element tabDiv = appendWithId(tabs, "div", tabId);
+            tabDiv.setAttribute("class", "tab");
+            appendWithText(tabDiv, "h2", tab.title);
+            tab.renderer.execute(tabDiv);
+        }
+    }
+
+    protected void addFailuresTab() {
+        if (!results.getFailures().isEmpty()) {
+            addTab("Failed tests", new Action<Element>() {
+                public void execute(Element element) {
+                    renderFailures(element);
+                }
+            });
+        }
+    }
+
+    protected void renderFailures(Element parent) {
+        Element ul = append(parent, "ul");
+        ul.setAttribute("class", "linkList");
+        for (TestResult test : results.getFailures()) {
+            Element li = append(ul, "li");
+            appendLink(li, String.format("%s.html", test.getClassResults().getName()), test.getClassResults().getSimpleName());
+            appendText(li, ".");
+            appendLink(li, String.format("%s.html#%s", test.getClassResults().getName(), test.getName()), test.getName());
+        }
+    }
+
+    protected Element append(Element parent, String name) {
+        Element element = parent.getOwnerDocument().createElement(name);
+        parent.appendChild(element);
+        return element;
+    }
+
+    protected Element appendWithId(Element parent, String name, String id) {
+        Element element = parent.getOwnerDocument().createElement(name);
+        parent.appendChild(element);
+        element.setAttribute("id", id);
+        return element;
+    }
+
+    protected Element appendWithText(Element parent, String name, Object textContent) {
+        Element element = parent.getOwnerDocument().createElement(name);
+        parent.appendChild(element);
+        appendText(element, textContent);
+        return element;
+    }
+
+    protected void appendText(Element element, Object textContent) {
+        element.appendChild(element.getOwnerDocument().createTextNode(textContent.toString()));
+    }
+
+    protected Element appendTableAndRow(Element parent) {
+        return append(append(parent, "table"), "tr");
+    }
+
+    protected Element appendCell(Element parent) {
+        return append(append(parent, "td"), "div");
+    }
+
+    protected Element appendLink(Element parent, String href, Object textContent) {
+        Element element = appendWithText(parent, "a", textContent);
+        element.setAttribute("href", href);
+        return element;
+    }
+
+    void render(Document document, T results) {
+        this.results = results;
+
+        registerTabs();
+
+        Element html = document.createElement("html");
+        document.appendChild(html);
+
+        // <head>
+        Element head = append(html, "head");
+        appendWithText(head, "title", String.format("Test results - %s", results.getTitle()));
+        Element link = append(head, "link");
+        link.setAttribute("rel", "stylesheet");
+        link.setAttribute("href", "style.css");
+        link.setAttribute("type", "text/css");
+        Element script = append(head, "script");
+        script.setAttribute("src", "report.js");
+        script.setAttribute("type", "text/javascript");
+
+        // <body>
+        Element body = append(html, "body");
+        Element content = appendWithId(body, "div", "content");
+        appendWithText(content, "h1", results.getTitle());
+        renderBreadcrumbs(content);
+
+        // summary
+        Element summary = appendWithId(content, "div", "summary");
+        Element row = appendTableAndRow(summary);
+        Element group = appendCell(row);
+        group.setAttribute("class", "summaryGroup");
+        Element summaryRow = appendTableAndRow(group);
+
+        Element tests = appendCell(summaryRow);
+        tests.setAttribute("id", "tests");
+        tests.setAttribute("class", "infoBox");
+        Element div = appendWithText(tests, "div", results.getTestCount());
+        div.setAttribute("class", "counter");
+        appendWithText(tests, "p", "tests");
+
+        Element failures = appendCell(summaryRow);
+        failures.setAttribute("id", "failures");
+        failures.setAttribute("class", "infoBox");
+        div = appendWithText(failures, "div", results.getFailureCount());
+        div.setAttribute("class", "counter");
+        appendWithText(failures, "p", "failures");
+
+        Element duration = appendCell(summaryRow);
+        duration.setAttribute("id", "duration");
+        duration.setAttribute("class", "infoBox");
+        div = appendWithText(duration, "div", results.getFormattedDuration());
+        div.setAttribute("class", "counter");
+        appendWithText(duration, "p", "duration");
+
+        Element successRate = appendCell(row);
+        successRate.setAttribute("id", "successRate");
+        successRate.setAttribute("class", String.format("infoBox %s", results.getStatusClass()));
+        div = appendWithText(successRate, "div", results.getFormattedSuccessRate());
+        div.setAttribute("class", "percent");
+        appendWithText(successRate, "p", "successful");
+
+        renderTabs(content);
+
+        Element footer = appendWithId(content, "div", "footer");
+        Element footerText = append(footer, "p");
+        appendText(footerText, "Generated by ");
+        appendLink(footerText, "http://www.gradle.org/", String.format("Gradle %s", GradleVersion.current().getVersion()));
+        appendText(footerText, String.format(" at %s", DateFormat.getDateTimeInstance().format(new Date())));
+    }
+
+    private static class TabDefinition {
+        private final String title;
+        private final Action<Element> renderer;
+
+        public TabDefinition(String title, Action<Element> renderer) {
+            this.title = title;
+            this.renderer = renderer;
+        }
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/TestFailure.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/TestFailure.java
new file mode 100644
index 0000000..b10b67e
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/TestFailure.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report;
+
+public class TestFailure {
+    private final String message;
+    private final String stackTrace;
+
+    public TestFailure(String message, String stackTrace) {
+        this.message = message;
+        this.stackTrace = stackTrace;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public String getStackTrace() {
+        return stackTrace;
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/TestReporter.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/TestReporter.java
new file mode 100644
index 0000000..3463885
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/TestReporter.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report;
+
+import java.io.File;
+
+public interface TestReporter {
+    void setTestResultsDir(File resultDir);
+
+    void setTestReportDir(File reportDir);
+
+    void generateReport();
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/TestResult.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/TestResult.java
new file mode 100644
index 0000000..8a4c72a
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/TestResult.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.gradle.api.tasks.testing.TestResult.ResultType;
+
+public class TestResult extends TestResultModel implements Comparable<TestResult> {
+    private final long duration;
+    final ClassTestResults classResults;
+    final List<TestFailure> failures = new ArrayList<TestFailure>();
+    final String name;
+    private boolean ignored;
+
+    public TestResult(String name, long duration, ClassTestResults classResults) {
+        this.name = name;
+        this.duration = duration;
+        this.classResults = classResults;
+    }
+
+    public Object getId() {
+        return name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String getTitle() {
+        return String.format("Test %s", name);
+    }
+
+    public ResultType getResultType() {
+        if (ignored) {
+            return ResultType.SKIPPED;
+        }
+        return failures.isEmpty() ? ResultType.SUCCESS : ResultType.FAILURE;
+    }
+
+    public long getDuration() {
+        return duration;
+    }
+
+    @Override
+    public String getFormattedDuration() {
+        return ignored ? "-" : super.getFormattedDuration();
+    }
+
+    public ClassTestResults getClassResults() {
+        return classResults;
+    }
+
+    public List<TestFailure> getFailures() {
+        return failures;
+    }
+
+    public void addFailure(String message, String stackTrace) {
+        classResults.failed(this);
+        failures.add(new TestFailure(message, stackTrace));
+    }
+
+    public void ignored() {
+        ignored = true;
+    }
+
+    public int compareTo(TestResult testResult) {
+        int diff = classResults.getName().compareTo(testResult.classResults.getName());
+        if (diff != 0) {
+            return diff;
+        }
+
+        diff = name.compareTo(testResult.name);
+        if (diff != 0) {
+            return diff;
+        }
+
+        Integer thisIdentity = System.identityHashCode(this);
+        int otherIdentity = System.identityHashCode(testResult);
+        return thisIdentity.compareTo(otherIdentity);
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/TestResultModel.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/TestResultModel.java
new file mode 100644
index 0000000..b6f33fb
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/TestResultModel.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report;
+
+import org.gradle.api.tasks.testing.TestResult;
+
+import java.math.BigDecimal;
+
+public abstract class TestResultModel {
+    public static final int MILLIS_PER_SECOND = 1000;
+    public static final int MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
+    public static final int MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
+    public static final int MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;
+
+    public abstract TestResult.ResultType getResultType();
+
+    public abstract long getDuration();
+
+    public abstract String getTitle();
+
+    public String getFormattedDuration() {
+        Long duration = getDuration();
+        if (duration == 0) {
+            return "0s";
+        }
+
+        StringBuilder result = new StringBuilder();
+
+        long days = duration / MILLIS_PER_DAY;
+        duration = duration % MILLIS_PER_DAY;
+        if (days > 0) {
+            result.append(days);
+            result.append("d");
+        }
+        long hours = duration / MILLIS_PER_HOUR;
+        duration = duration % MILLIS_PER_HOUR;
+        if (hours > 0 || result.length() > 0) {
+            result.append(hours);
+            result.append("h");
+        }
+        long minutes = duration / MILLIS_PER_MINUTE;
+        duration = duration % MILLIS_PER_MINUTE;
+        if (minutes > 0 || result.length() > 0) {
+            result.append(minutes);
+            result.append("m");
+        }
+        int secondsScale = result.length() > 0 ? 2 : 3;
+        result.append(BigDecimal.valueOf(duration).divide(BigDecimal.valueOf(MILLIS_PER_SECOND)).setScale(secondsScale, BigDecimal.ROUND_HALF_UP));
+        result.append("s");
+        return result.toString();
+    }
+
+    public String getStatusClass() {
+        switch (getResultType()) {
+            case SUCCESS:
+                return "success";
+            case FAILURE:
+                return "failures";
+            case SKIPPED:
+                return "skipped";
+            default:
+                throw new IllegalStateException();
+        }
+    }
+
+    public String getFormattedResultType() {
+        switch (getResultType()) {
+            case SUCCESS:
+                return "passed";
+            case FAILURE:
+                return "failed";
+            case SKIPPED:
+                return "ignored";
+            default:
+                throw new IllegalStateException();
+        }
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/MaxNParallelTestClassProcessor.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/MaxNParallelTestClassProcessor.java
index 4cd1f25..c028311 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/MaxNParallelTestClassProcessor.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/MaxNParallelTestClassProcessor.java
@@ -35,7 +35,7 @@ import java.util.List;
  */
 public class MaxNParallelTestClassProcessor implements TestClassProcessor {
     private final int maxProcessors;
-    private final Factory<? extends TestClassProcessor> factory;
+    private final Factory<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, Factory<? extends TestClassProcessor> factory, ActorFactory actorFactory) {
+    public MaxNParallelTestClassProcessor(int maxProcessors, Factory<TestClassProcessor> factory, ActorFactory actorFactory) {
         this.maxProcessors = maxProcessors;
         this.factory = factory;
         this.actorFactory = actorFactory;
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/RestartEveryNTestClassProcessor.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/RestartEveryNTestClassProcessor.java
index 38df380..4fc5c24 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/RestartEveryNTestClassProcessor.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/RestartEveryNTestClassProcessor.java
@@ -1,66 +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.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;
-
-public class RestartEveryNTestClassProcessor implements TestClassProcessor {
-    private final Factory<? extends TestClassProcessor> factory;
-    private final long restartEvery;
-    private long testCount;
-    private TestClassProcessor processor;
-    private TestResultProcessor resultProcessor;
-
-    public RestartEveryNTestClassProcessor(Factory<? extends TestClassProcessor> factory, long restartEvery) {
-        this.factory = factory;
-        this.restartEvery = restartEvery;
-    }
-
-    public void startProcessing(TestResultProcessor resultProcessor) {
-        this.resultProcessor = resultProcessor;
-    }
-
-    public void processTestClass(TestClassRunInfo testClass) {
-        if (processor == null) {
-            processor = factory.create();
-            processor.startProcessing(resultProcessor);
-        }
-        processor.processTestClass(testClass);
-        testCount++;
-        if (testCount == restartEvery) {
-            endBatch();
-        }
-    }
-
-    public void stop() {
-        if (processor != null) {
-            endBatch();
-        }
-    }
-
-    private void endBatch() {
-        try {
-            processor.stop();
-        } finally {
-            processor = null;
-            testCount = 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.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;
+
+public class RestartEveryNTestClassProcessor implements TestClassProcessor {
+    private final Factory<TestClassProcessor> factory;
+    private final long restartEvery;
+    private long testCount;
+    private TestClassProcessor processor;
+    private TestResultProcessor resultProcessor;
+
+    public RestartEveryNTestClassProcessor(Factory<TestClassProcessor> factory, long restartEvery) {
+        this.factory = factory;
+        this.restartEvery = restartEvery;
+    }
+
+    public void startProcessing(TestResultProcessor resultProcessor) {
+        this.resultProcessor = resultProcessor;
+    }
+
+    public void processTestClass(TestClassRunInfo testClass) {
+        if (processor == null) {
+            processor = factory.create();
+            processor.startProcessing(resultProcessor);
+        }
+        processor.processTestClass(testClass);
+        testCount++;
+        if (testCount == restartEvery) {
+            endBatch();
+        }
+    }
+
+    public void stop() {
+        if (processor != null) {
+            endBatch();
+        }
+    }
+
+    private void endBatch() {
+        try {
+            processor.stop();
+        } finally {
+            processor = null;
+            testCount = 0;
+        }
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/results/TestLogger.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/results/TestLogger.java
index 4d96dc5..7ef1d6a 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/results/TestLogger.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/results/TestLogger.java
@@ -71,7 +71,9 @@ public class TestLogger implements TestListener {
 
     public void beforeSuite(TestDescriptor suite) {
         if (suite.getParent() == null) {
-            progressLogger = factory.start(TestLogger.class.getName());
+            progressLogger = factory.newOperation(TestLogger.class);
+            progressLogger.setDescription("Run tests");
+            progressLogger.started();
         }
     }
 
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/worker/ForkingTestClassProcessor.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/worker/ForkingTestClassProcessor.java
index abd382c..6a46207 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/worker/ForkingTestClassProcessor.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/worker/ForkingTestClassProcessor.java
@@ -1,80 +1,80 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.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;
-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 java.io.File;
-
-public class ForkingTestClassProcessor implements TestClassProcessor {
-    private final Factory<? extends WorkerProcessBuilder> workerFactory;
-    private final WorkerTestClassProcessorFactory processorFactory;
-    private final JavaForkOptions options;
-    private final Iterable<File> classPath;
-    private final Action<WorkerProcessBuilder> buildConfigAction;
-    private RemoteTestClassProcessor remoteProcessor;
-    private WorkerProcess workerProcess;
-    private TestResultProcessor resultProcessor;
-
-    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;
-        this.classPath = classPath;
-        this.buildConfigAction = buildConfigAction;
-    }
-
-    public void startProcessing(TestResultProcessor resultProcessor) {
-        this.resultProcessor = resultProcessor;
-    }
-
-    public void processTestClass(TestClassRunInfo testClass) {
-        if (remoteProcessor == null) {
-            WorkerProcessBuilder builder = workerFactory.create();
-            builder.applicationClasspath(classPath);
-            builder.setLoadApplicationInSystemClassLoader(true);
-            builder.worker(new TestWorker(processorFactory));
-            options.copyTo(builder.getJavaCommand());
-            buildConfigAction.execute(builder);
-            
-            workerProcess = builder.build();
-            workerProcess.start();
-
-            workerProcess.getConnection().addIncoming(TestResultProcessor.class, resultProcessor);
-            remoteProcessor = workerProcess.getConnection().addOutgoing(RemoteTestClassProcessor.class);
-
-            remoteProcessor.startProcessing();
-        }
-
-        remoteProcessor.processTestClass(testClass);
-    }
-
-    public void stop() {
-        if (remoteProcessor != null) {
-            remoteProcessor.stop();
-            workerProcess.waitForStop();
-        }
-    }
-}
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.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;
+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 java.io.File;
+
+public class ForkingTestClassProcessor implements TestClassProcessor {
+    private final Factory<WorkerProcessBuilder> workerFactory;
+    private final WorkerTestClassProcessorFactory processorFactory;
+    private final JavaForkOptions options;
+    private final Iterable<File> classPath;
+    private final Action<WorkerProcessBuilder> buildConfigAction;
+    private RemoteTestClassProcessor remoteProcessor;
+    private WorkerProcess workerProcess;
+    private TestResultProcessor resultProcessor;
+
+    public ForkingTestClassProcessor(Factory<WorkerProcessBuilder> workerFactory, WorkerTestClassProcessorFactory processorFactory, JavaForkOptions options, Iterable<File> classPath, Action<WorkerProcessBuilder> buildConfigAction) {
+        this.workerFactory = workerFactory;
+        this.processorFactory = processorFactory;
+        this.options = options;
+        this.classPath = classPath;
+        this.buildConfigAction = buildConfigAction;
+    }
+
+    public void startProcessing(TestResultProcessor resultProcessor) {
+        this.resultProcessor = resultProcessor;
+    }
+
+    public void processTestClass(TestClassRunInfo testClass) {
+        if (remoteProcessor == null) {
+            WorkerProcessBuilder builder = workerFactory.create();
+            builder.applicationClasspath(classPath);
+            builder.setLoadApplicationInSystemClassLoader(true);
+            builder.worker(new TestWorker(processorFactory));
+            options.copyTo(builder.getJavaCommand());
+            buildConfigAction.execute(builder);
+            
+            workerProcess = builder.build();
+            workerProcess.start();
+
+            workerProcess.getConnection().addIncoming(TestResultProcessor.class, resultProcessor);
+            remoteProcessor = workerProcess.getConnection().addOutgoing(RemoteTestClassProcessor.class);
+
+            remoteProcessor.startProcessing();
+        }
+
+        remoteProcessor.processTestClass(testClass);
+    }
+
+    public void stop() {
+        if (remoteProcessor != null) {
+            remoteProcessor.stop();
+            workerProcess.waitForStop();
+        }
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/ApplicationPlugin.groovy b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/ApplicationPlugin.groovy
new file mode 100644
index 0000000..9c1eb2c
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/ApplicationPlugin.groovy
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.Plugin
+import org.gradle.api.Project
+import org.gradle.api.file.CopySpec
+import org.gradle.api.tasks.JavaExec
+import org.gradle.api.tasks.Sync
+import org.gradle.api.tasks.application.CreateStartScripts
+import org.gradle.api.tasks.bundling.Zip
+import org.gradle.api.GradleException
+
+/**
+ * <p>A {@link Plugin} which runs a project as a Java Application.</p>
+ *
+ * @author Rene Groeschke
+ */
+class ApplicationPlugin implements Plugin<Project> {
+    static final String APPLICATION_PLUGIN_NAME = "application"
+    static final String APPLICATION_GROUP = APPLICATION_PLUGIN_NAME
+
+    static final String TASK_RUN_NAME = "run"
+    static final String TASK_START_SCRIPTS_NAME = "startScripts"
+    static final String TASK_INSTALL_NAME = "installApp"
+    static final String TASK_DIST_ZIP_NAME = "distZip"
+
+    private Project project
+    private ApplicationPluginConvention pluginConvention
+
+    void apply(final Project project) {
+        this.project = project
+        project.plugins.apply(JavaPlugin)
+
+        addPluginConvention()
+        addRunTask()
+        addCreateScriptsTask()
+        addInstallTask()
+        addDistZipTask()
+    }
+
+    private void addPluginConvention() {
+        pluginConvention = new ApplicationPluginConvention()
+        pluginConvention.applicationName = project.name
+        project.convention.plugins.application = pluginConvention
+    }
+
+    private void addRunTask() {
+        def run = project.tasks.add(TASK_RUN_NAME, JavaExec)
+        run.description = "Runs this project as a JVM application"
+        run.group = APPLICATION_GROUP
+        run.classpath = project.sourceSets.main.runtimeClasspath
+        run.conventionMapping.main = { pluginConvention.mainClassName }
+    }
+
+    // @Todo: refactor this task configuration to extend a copy task and use replace tokens
+    private void addCreateScriptsTask() {
+        def startScripts = project.tasks.add(TASK_START_SCRIPTS_NAME, CreateStartScripts)
+        startScripts.description = "Creates OS specific scripts to run the project as a JVM application."
+        startScripts.classpath = project.tasks[JavaPlugin.JAR_TASK_NAME].outputs.files + project.configurations.runtime
+        startScripts.conventionMapping.mainClassName = { pluginConvention.mainClassName }
+        startScripts.conventionMapping.applicationName = { pluginConvention.applicationName }
+        startScripts.conventionMapping.outputDir = { new File(project.buildDir, 'scripts') }
+    }
+
+    private void addInstallTask() {
+        def installTask = project.tasks.add(TASK_INSTALL_NAME, Sync)
+        installTask.description = "Installs the project as a JVM application along with libs and OS specific scripts."
+        installTask.group = APPLICATION_GROUP
+        installTask.with(createDistSpec())
+        installTask.into { project.file("${project.buildDir}/install/${pluginConvention.applicationName}") }
+        installTask.doFirst {
+            if (destinationDir.directory) {
+                if (!new File(destinationDir, 'lib').directory || !new File(destinationDir, 'bin').directory) {
+                    throw new GradleException("The specified installation directory '${destinationDir}' is neither empty nor does it contain an installation for '${pluginConvention.applicationName}'.\n" +
+                            "If you really want to install to this directory, delete it and run the install task again.\n" +
+                            "Alternatively, choose a different installation directory."
+                    )
+                }
+            }
+        }
+        installTask.doLast {
+            project.ant.chmod(file: "${destinationDir.absolutePath}/bin/${pluginConvention.applicationName}", perm: 'ugo+x')
+        }
+    }
+
+    private void addDistZipTask() {
+        def distZipTask = project.tasks.add(TASK_DIST_ZIP_NAME, Zip)
+        distZipTask.description = "Bundles the project as a JVM application with libs and OS specific scripts."
+        distZipTask.group = APPLICATION_GROUP
+        distZipTask.conventionMapping.baseName = { pluginConvention.applicationName }
+        def baseDir = { distZipTask.archiveName - ".zip" }
+        distZipTask.into(baseDir) {
+            with(createDistSpec())
+        }
+    }
+
+    private CopySpec createDistSpec() {
+        def jar = project.tasks[JavaPlugin.JAR_TASK_NAME]
+        def startScripts = project.tasks[TASK_START_SCRIPTS_NAME]
+
+        project.copySpec {
+            into("lib") {
+                from(jar.outputs.files)
+                from(project.configurations.runtime)
+            }
+            into("bin") {
+                from(startScripts.outputs.files)
+                fileMode = 0755
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/ApplicationPluginConvention.groovy b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/ApplicationPluginConvention.groovy
new file mode 100644
index 0000000..3ecc9d4
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/ApplicationPluginConvention.groovy
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+ /**
+ * <p>A {@link Convention} used for the ApplicationPlugin.</p>
+ *
+ * @author Rene Groeschke
+ */
+class ApplicationPluginConvention {
+    /**
+     * The name of the application.
+     */
+    String applicationName
+
+    /**
+     * The fully qualified name of the application's main class.
+     */
+    String mainClassName
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/BasePlugin.groovy b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/BasePlugin.groovy
index 69cdf1d..aa00187 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/BasePlugin.groovy
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/BasePlugin.groovy
@@ -87,7 +87,11 @@ class BasePlugin implements Plugin<Project> {
                     if (!taskName.startsWith(prefix)) {
                         return
                     }
-                    Task task = project.tasks.findByName(StringUtils.uncapitalize(taskName.substring(prefix.length())))
+                    String targetTaskName = taskName.substring(prefix.length())
+                    if (targetTaskName.charAt(0).isLowerCase()) {
+                        return
+                    }
+                    Task task = project.tasks.findByName(StringUtils.uncapitalize(targetTaskName))
                     if (task == null) {
                         return
                     }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/GroovyBasePlugin.java b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/GroovyBasePlugin.java
index 36f3d06..8d46d2e 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/GroovyBasePlugin.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/GroovyBasePlugin.java
@@ -79,8 +79,8 @@ public class GroovyBasePlugin implements Plugin<Project> {
                         return groovySourceSet.getGroovy().contains(element.getFile());
                     }
                 });
-                sourceSet.getAllJava().add(groovySourceSet.getGroovy().matching(sourceSet.getJava().getFilter()));
-                sourceSet.getAllSource().add(groovySourceSet.getGroovy());
+                sourceSet.getAllJava().source(groovySourceSet.getGroovy());
+                sourceSet.getAllSource().source(groovySourceSet.getGroovy());
 
                 String compileTaskName = sourceSet.getCompileTaskName("groovy");
                 GroovyCompile compile = project.getTasks().add(compileTaskName, GroovyCompile.class);
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/GroovyPlugin.java b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/GroovyPlugin.java
index 95b6f15..8a4b6a6 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/GroovyPlugin.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/GroovyPlugin.java
@@ -23,8 +23,6 @@ import org.gradle.api.tasks.GroovySourceSet;
 import org.gradle.api.tasks.SourceSet;
 import org.gradle.api.tasks.javadoc.Groovydoc;
 
-import static org.gradle.api.plugins.JavaPlugin.COMPILE_CONFIGURATION_NAME;
-
 /**
  * <p>A {@link Plugin} which extends the {@link JavaPlugin} to provide support for compiling and documenting Groovy
  * source files.</p>
@@ -37,26 +35,26 @@ public class GroovyPlugin implements Plugin<Project> {
     public void apply(Project project) {
         project.getPlugins().apply(GroovyBasePlugin.class);
         project.getPlugins().apply(JavaPlugin.class);
+        configureConfigurations(project);
+        configureGroovydoc(project);
+    }
 
-        project.getConfigurations().getByName(COMPILE_CONFIGURATION_NAME).extendsFrom(
+    private void configureConfigurations(Project project) {
+        project.getConfigurations().getByName(JavaPlugin.COMPILE_CONFIGURATION_NAME).extendsFrom(
                 project.getConfigurations().getByName(GroovyBasePlugin.GROOVY_CONFIGURATION_NAME)
         );
-        configureGroovydoc(project);
     }
 
     private void configureGroovydoc(final Project project) {
         Groovydoc groovyDoc = project.getTasks().add(GROOVYDOC_TASK_NAME, Groovydoc.class);
-        groovyDoc.setDescription("Generates the groovydoc for the main source code.");
+        groovyDoc.setDescription("Generates Groovydoc API documentation for the main source code.");
         groovyDoc.setGroup(JavaBasePlugin.DOCUMENTATION_GROUP);
-        groovyDoc.setSource(mainGroovy(project.getConvention()).getGroovy());
-    }
 
-    private SourceSet main(Convention convention) {
-        return convention.getPlugin(JavaPluginConvention.class).getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME);
-    }
+        JavaPluginConvention convention = project.getConvention().getPlugin(JavaPluginConvention.class);
+        SourceSet sourceSet = convention.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME);
+        groovyDoc.setClasspath(sourceSet.getClasses().plus(sourceSet.getCompileClasspath()));
 
-    private GroovySourceSet mainGroovy(Convention convention) {
-        return ((DynamicObjectAware) main(convention)).getConvention().getPlugin(GroovySourceSet.class);
+        GroovySourceSet groovySourceSet = ((DynamicObjectAware) sourceSet).getConvention().getPlugin(GroovySourceSet.class);
+        groovyDoc.setSource(groovySourceSet.getGroovy());
     }
-
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/JavaPlugin.java b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/JavaPlugin.java
index 1d32a85..1c8f75a 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/JavaPlugin.java
+++ b/subprojects/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 main source code.");
+        javadoc.setDescription("Generates Javadoc API documentation for the main source code.");
         javadoc.setGroup(JavaBasePlugin.DOCUMENTATION_GROUP);
         javadoc.setClasspath(mainSourceSet.getClasses().plus(mainSourceSet.getCompileClasspath()));
         javadoc.setSource(mainSourceSet.getAllJava());
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/GroovySourceSet.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/GroovySourceSet.java
index 7910e71..babf0cc 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/GroovySourceSet.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/GroovySourceSet.java
@@ -16,7 +16,6 @@
 package org.gradle.api.tasks;
 
 import groovy.lang.Closure;
-import org.gradle.api.file.FileTree;
 import org.gradle.api.file.SourceDirectorySet;
 
 /**
@@ -47,5 +46,5 @@ public interface GroovySourceSet {
      *
      * @return the Groovy source. Never returns null.
      */
-    FileTree getAllGroovy();
+    SourceDirectorySet getAllGroovy();
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/SourceSet.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/SourceSet.java
index 701cffc..6077a19 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/SourceSet.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/SourceSet.java
@@ -17,7 +17,6 @@ package org.gradle.api.tasks;
 
 import groovy.lang.Closure;
 import org.gradle.api.file.FileCollection;
-import org.gradle.api.file.FileTree;
 import org.gradle.api.file.SourceDirectorySet;
 
 import java.io.File;
@@ -141,14 +140,14 @@ public interface SourceSet {
      *
      * @return the Java source. Never returns null.
      */
-    FileTree getAllJava();
+    SourceDirectorySet getAllJava();
 
     /**
      * All source files for this source set.
      *
      * @return the source. Never returns null.
      */
-    FileTree getAllSource();
+    SourceDirectorySet getAllSource();
 
     /**
      * Returns the name of the classes task for this source set.
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/application/CreateStartScripts.groovy b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/application/CreateStartScripts.groovy
new file mode 100644
index 0000000..77f96f7
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/application/CreateStartScripts.groovy
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.application
+
+import groovy.text.SimpleTemplateEngine
+import org.gradle.api.file.FileCollection
+import org.gradle.api.internal.ConventionTask
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.TaskAction
+import org.gradle.util.GUtil
+import org.gradle.util.TextUtil
+
+/**
+ * <p>A {@link org.gradle.api.Task} for creating OS dependent start scripts.</p>
+ *
+ * @author Rene Groeschke
+ */
+class CreateStartScripts extends ConventionTask {
+
+    /**
+     * The directory to write the scripts into.
+     */
+    File outputDir
+
+    /**
+     * The application's main class.
+     */
+    @Input
+    String mainClassName
+
+    /**
+     * The application's name.
+     */
+    @Input
+    String applicationName
+
+    String optsEnvironmentVar
+
+    String exitEnvironmentVar
+
+    /**
+     * The class path for the application.
+     */
+    @InputFiles
+    FileCollection classpath
+
+    /**
+     * Returns the name of the application's OPTS environment variable.
+     */
+    @Input
+    String getOptsEnvironmentVar() {
+        if (optsEnvironmentVar) {
+            return optsEnvironmentVar
+        }
+        if (!getApplicationName()) {
+            return null
+        }
+        return "${GUtil.toConstant(getApplicationName())}_OPTS"
+    }
+
+    @Input
+    String getExitEnvironmentVar() {
+        if (exitEnvironmentVar) {
+            return exitEnvironmentVar
+        }
+        if (!getApplicationName()) {
+            return null
+        }
+        return "${GUtil.toConstant(getApplicationName())}_EXIT_CONSOLE"
+    }
+
+    @OutputFile
+    File getUnixScript() {
+        return new File(getOutputDir(), getApplicationName())
+    }
+
+    @OutputFile
+    File getWindowsScript() {
+        return new File(getOutputDir(), "${getApplicationName()}.bat")
+    }
+
+    @TaskAction
+    void generate() {
+        getOutputDir().mkdirs()
+
+        //ref all files in classpath
+        def unixLibPath = "\$APP_HOME/lib/"
+        def unixClassPath = new StringBuffer()
+
+        def windowsLibPath = "%APP_HOME%\\lib\\"
+        def windowsClassPath = new StringBuffer()
+
+        classpath.each {
+            unixClassPath << "$unixLibPath${it.name}:"
+            windowsClassPath << "$windowsLibPath${it.name};"
+        }
+
+        generateUnixScript(
+                applicationName: getApplicationName(),
+                optsEnvironmentVar: getOptsEnvironmentVar(),
+                mainClassName: getMainClassName(),
+                classpath: unixClassPath)
+        generateWindowsScript(
+                applicationName: getApplicationName(),
+                optsEnvironmentVar: getOptsEnvironmentVar(),
+                exitEnvironmentVar: getExitEnvironmentVar(),
+                mainClassName: getMainClassName(),
+                classpath: windowsClassPath)
+    }
+
+    private void generateUnixScript(Map binding) {
+        generateScript('unixStartScript.txt', binding, TextUtil.unixLineSeparator, getUnixScript())
+    }
+
+    private void generateWindowsScript(Map binding) {
+        generateScript('windowsStartScript.txt', binding, TextUtil.windowsLineSeparator, getWindowsScript())
+    }
+
+    private void generateScript(String templateName, Map binding, String lineSeparator, File outputFile) {
+        def engine = new SimpleTemplateEngine()
+        def templateText = CreateStartScripts.getResourceAsStream(templateName).text
+        def output = engine.createTemplate(templateText).make(binding)
+        def nativeOutput = TextUtil.convertLineSeparators(output as String, lineSeparator)
+        outputFile.write(nativeOutput)
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/bundling/Jar.groovy b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/bundling/Jar.groovy
index 92c2065..f9e6629 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/bundling/Jar.groovy
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/bundling/Jar.groovy
@@ -17,12 +17,13 @@
 package org.gradle.api.tasks.bundling
 
 import org.gradle.api.file.CopySpec
-import org.gradle.api.internal.file.MapFileTree
+import org.gradle.api.internal.file.collections.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
 import org.gradle.api.java.archives.Manifest
+import org.gradle.api.internal.file.collections.FileTreeAdapter
 
 /**
  * Assembles a JAR archive.
@@ -46,7 +47,7 @@ public class Jar extends Zip {
                 Manifest manifest = getManifest() ?: new DefaultManifest(null)
                 manifest.writeTo(new OutputStreamWriter(outstr))
             }
-            manifestSource
+            return new FileTreeAdapter(manifestSource)
         }
         copyAction.mainSpec.eachFile { FileCopyDetails details ->
             if (details.path.equalsIgnoreCase('META-INF/MANIFEST.MF')) {
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/Compile.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/Compile.java
index 59a7457..4ef3a35 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/Compile.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/Compile.java
@@ -40,8 +40,8 @@ public class Compile extends AbstractCompile {
     private File dependencyCacheDir;
 
     public Compile() {
-        Factory<? extends AntBuilder> antBuilderFactory = getServices().getFactory(AntBuilder.class);
-        javaCompiler = new IncrementalJavaCompiler(new AntJavaCompiler((Factory) antBuilderFactory), antBuilderFactory, getOutputs());
+        Factory<AntBuilder> antBuilderFactory = getServices().getFactory(AntBuilder.class);
+        javaCompiler = new IncrementalJavaCompiler(new AntJavaCompiler(antBuilderFactory), antBuilderFactory, getOutputs());
     }
 
     @TaskAction
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/CompileOptions.groovy b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/CompileOptions.groovy
index 3c50457..2499e1c 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/CompileOptions.groovy
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/CompileOptions.groovy
@@ -64,7 +64,7 @@ class CompileOptions extends AbstractOptions {
     DebugOptions debugOptions = new DebugOptions()
 
     /**
-     * Specifies whether to run the compiler in a child process. The default is {@code false.
+     * Specifies whether to run the compiler in a child process. The default is {@code false}.
      */
     boolean fork = false
 
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/AntGroovydoc.groovy b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/AntGroovydoc.groovy
index 09bd496..60c0c42 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/AntGroovydoc.groovy
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/AntGroovydoc.groovy
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-
-
 package org.gradle.api.tasks.javadoc
 
 import org.gradle.api.Project
@@ -31,33 +29,33 @@ class AntGroovydoc {
     private final ClassPathRegistry classPathRegistry
 
     def AntGroovydoc(IsolatedAntBuilder ant, ClassPathRegistry classPathRegistry) {
-        this.ant = ant;
-        this.classPathRegistry = classPathRegistry;
+        this.ant = ant
+        this.classPathRegistry = classPathRegistry
     }
 
     void execute(FileCollection source, File destDir, boolean use, String windowTitle,
-                 String docTitle, String header, String footer, String overview, boolean includePrivate, Set links,
-                 List groovyClasspath, Project project) {
+                 String docTitle, String header, String footer, String overview, boolean includePrivate,
+                 Set links, Iterable groovyClasspath, Iterable classpath, Project project) {
 
-        File tmpDir = new File(project.buildDir, "tmp/groovydoc")
+        def tmpDir = new File(project.buildDir, "tmp/groovydoc")
         project.delete tmpDir
         project.copy {
             from source
             into tmpDir
         }
 
-        Map args = [:]
+        def args = [:]
         args.sourcepath = tmpDir.toString()
         args.destdir = destDir
         args.use = use
         args['private'] = includePrivate
-        addToMapIfNotNull(args, 'windowtitle', windowTitle)
-        addToMapIfNotNull(args, 'doctitle', docTitle)
-        addToMapIfNotNull(args, 'header', header)
-        addToMapIfNotNull(args, 'footer', footer)
-        addToMapIfNotNull(args, 'overview', overview)
+        putIfNotNull(args, 'windowtitle', windowTitle)
+        putIfNotNull(args, 'doctitle', docTitle)
+        putIfNotNull(args, 'header', header)
+        putIfNotNull(args, 'footer', footer)
+        putIfNotNull(args, 'overview', overview)
 
-        ant.withGroovy(groovyClasspath).execute {
+        ant.withGroovy(groovyClasspath).withClasspath(classpath).execute {
             taskdef(name: 'groovydoc', classname: 'org.codehaus.groovy.ant.Groovydoc')
             groovydoc(args) {
                 links.each {gradleLink ->
@@ -67,7 +65,7 @@ class AntGroovydoc {
         }
     }
 
-    void addToMapIfNotNull(Map map, String key, Object value) {
+    void putIfNotNull(Map map, String key, Object value) {
         if (value != null) {
             map.put(key, value)
         }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/Groovydoc.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/Groovydoc.java
index 0911f26..6144cc8 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/Groovydoc.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/Groovydoc.java
@@ -40,6 +40,8 @@ import java.util.*;
 public class Groovydoc extends SourceTask {
     private FileCollection groovyClasspath;
 
+    private FileCollection classpath;
+
     private File destinationDir;
 
     private AntGroovydoc antGroovydoc;
@@ -69,22 +71,21 @@ public class Groovydoc extends SourceTask {
 
     @TaskAction
     protected void generate() {
-        List<File> taskClasspath = new ArrayList<File>(getGroovyClasspath().getFiles());
-        throwExceptionIfTaskClasspathIsEmpty(taskClasspath);
+        checkGroovyClasspathNonEmpty(getGroovyClasspath().getFiles());
         antGroovydoc.execute(getSource(), getDestinationDir(), isUse(), getWindowTitle(), getDocTitle(), getHeader(),
-                getFooter(), getOverview(), isIncludePrivate(), getLinks(), taskClasspath, getProject());
+                getFooter(), getOverview(), isIncludePrivate(), getLinks(), getGroovyClasspath(), getClasspath(), getProject());
     }
 
-    private void throwExceptionIfTaskClasspathIsEmpty(List taskClasspath) {
-        if (taskClasspath.size() == 0) {
+    private void checkGroovyClasspathNonEmpty(Collection<File> classpath) {
+        if (classpath.isEmpty()) {
             throw new InvalidUserDataException("You must assign a Groovy library to the groovy configuration!");
         }
     }
 
     /**
-     * <p>Returns the directory to generate the documentation into.</p>
+     * Returns the directory to generate the documentation into.
      *
-     * @return The directory.
+     * @return The directory to generate the documentation into
      */
     @OutputDirectory
     public File getDestinationDir() {
@@ -92,16 +93,16 @@ public class Groovydoc extends SourceTask {
     }
 
     /**
-     * <p>Sets the directory to generate the documentation into.</p>
+     * Sets the directory to generate the documentation into.
      */
     public void setDestinationDir(File destinationDir) {
         this.destinationDir = destinationDir;
     }
 
     /**
-     * <p>Returns the classpath to use to locate classes referenced by the documented source.</p>
+     * Returns the classpath used to locate classes referenced by the documented sources.
      *
-     * @return The classpath.
+     * @return The classpath used to locate classes referenced by the documented sources
      */
     @InputFiles
     public FileCollection getGroovyClasspath() {
@@ -109,12 +110,29 @@ public class Groovydoc extends SourceTask {
     }
 
     /**
-     * <p>Sets the classpath to use to locate classes referenced by the documented source.</p>
+     * Sets the classpath used to locate classes referenced by the documented sources.
      */
     public void setGroovyClasspath(FileCollection groovyClasspath) {
         this.groovyClasspath = groovyClasspath;
     }
 
+    /**
+     * Returns the classpath containing the Groovy library to be used.
+     *
+     * @return The classpath containing the Groovy library to be used
+     */
+    @InputFiles
+    public FileCollection getClasspath() {
+        return classpath;
+    }
+
+    /**
+     * Sets the classpath containing the Groovy library to be used.
+     */
+    public void setClasspath(FileCollection classpath) {
+        this.classpath = classpath;
+    }
+
     public AntGroovydoc getAntGroovydoc() {
         return antGroovydoc;
     }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/Javadoc.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/Javadoc.java
index 62a91f0..847151b 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/Javadoc.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/Javadoc.java
@@ -18,11 +18,10 @@ package org.gradle.api.tasks.javadoc;
 
 import org.gradle.api.GradleException;
 import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.file.SimpleFileCollection;
 import org.gradle.api.tasks.*;
-import org.gradle.external.javadoc.internal.JavadocExecHandleBuilder;
 import org.gradle.external.javadoc.MinimalJavadocOptions;
 import org.gradle.external.javadoc.StandardJavadocDocletOptions;
+import org.gradle.external.javadoc.internal.JavadocExecHandleBuilder;
 import org.gradle.process.internal.ExecAction;
 import org.gradle.process.internal.ExecException;
 import org.gradle.util.GUtil;
@@ -34,6 +33,38 @@ import java.util.List;
 
 /**
  * <p>Generates HTML API documentation for Java classes.</p>
+ * <p>
+ * If you create your own Javadoc tasks remember to specify the 'source' property!
+ * Without source the javadoc task will not create any documentation. Example:
+ * <pre autoTested=''>
+ * apply plugin: 'java'
+ *
+ * task myJavadocs(type: Javadoc) {
+ *   source = sourceSets.main.allJava
+ * }
+ * </pre>
+ *
+ * <p>
+ * An example how to create a task that runs a custom doclet implementation:
+ * <pre autoTested=''>
+ * apply plugin: 'java'
+ *
+ * configurations {
+ *   jaxDoclet
+ * }
+ *
+ * dependencies {
+ *   //jaxDoclet "some.interesting:Dependency:1.0"
+ * }
+ *
+ * task generateRestApiDocs(type: Javadoc) {
+ *   source = sourceSets.main.allJava
+ *   destinationDir = file("${reportsDir.absolutePath}/rest-api-docs")
+ *   options.docletpath = configurations.jaxDoclet.files.asType(List)
+ *   options.doclet = "com.lunatech.doclets.jax.jaxrs.JAXRSDoclet"
+ *   options.addStringOption("jaxrscontext", "http://localhost:8080/myapp")
+ * }
+ * </pre>
  *
  * @author Hans Dockter
  */
@@ -50,7 +81,7 @@ public class Javadoc extends SourceTask {
 
     private MinimalJavadocOptions options = new StandardJavadocDocletOptions();
 
-    private FileCollection classpath = new SimpleFileCollection();
+    private FileCollection classpath = getProject().files();
 
     private String executable;
 
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/Test.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/Test.java
index ec8f934..d78edc0 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/Test.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/Test.java
@@ -279,7 +279,6 @@ public class Test extends ConventionTask implements JavaForkOptions, PatternFilt
     /**
      * {@inheritDoc}
      */
-    @Input
     public Map<String, Object> getEnvironment() {
         return options.getEnvironment();
     }
diff --git a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/application.properties b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/application.properties
new file mode 100644
index 0000000..c037c27
--- /dev/null
+++ b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/application.properties
@@ -0,0 +1 @@
+implementation-class=org.gradle.api.plugins.ApplicationPlugin
diff --git a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/base.properties b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/base.properties
index 6123954..38e51dd 100644
--- a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/base.properties
+++ b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/base.properties
@@ -1,16 +1 @@
-#
-# Copyright 2010 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-implementation-class=org.gradle.api.plugins.BasePlugin
+implementation-class=org.gradle.api.plugins.BasePlugin
diff --git a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/groovy-base.properties b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/groovy-base.properties
index 0a6c50b..87a18c3 100644
--- a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/groovy-base.properties
+++ b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/groovy-base.properties
@@ -1,16 +1 @@
-#
-# Copyright 2010 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-implementation-class=org.gradle.api.plugins.GroovyBasePlugin
+implementation-class=org.gradle.api.plugins.GroovyBasePlugin
diff --git a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/groovy.properties b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/groovy.properties
index 96ee710..d5b6bd9 100644
--- a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/groovy.properties
+++ b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/groovy.properties
@@ -1,16 +1 @@
-#
-# Copyright 2010 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-implementation-class=org.gradle.api.plugins.GroovyPlugin
+implementation-class=org.gradle.api.plugins.GroovyPlugin
diff --git a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/java-base.properties b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/java-base.properties
index 004c545..31bd892 100644
--- a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/java-base.properties
+++ b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/java-base.properties
@@ -1,16 +1 @@
-#
-# Copyright 2010 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-implementation-class=org.gradle.api.plugins.JavaBasePlugin
+implementation-class=org.gradle.api.plugins.JavaBasePlugin
diff --git a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/java.properties b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/java.properties
index 053a8d8..ff48cad 100644
--- a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/java.properties
+++ b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/java.properties
@@ -1,16 +1 @@
-#
-# Copyright 2010 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-implementation-class=org.gradle.api.plugins.JavaPlugin
+implementation-class=org.gradle.api.plugins.JavaPlugin
diff --git a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/project-report.properties b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/project-report.properties
index 19dd923..ab89ee7 100644
--- a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/project-report.properties
+++ b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/project-report.properties
@@ -1,16 +1 @@
-#
-# Copyright 2010 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-implementation-class=org.gradle.api.plugins.ProjectReportsPlugin
+implementation-class=org.gradle.api.plugins.ProjectReportsPlugin
diff --git a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/project-reports.properties b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/project-reports.properties
index d01711c..9d6bd13 100644
--- a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/project-reports.properties
+++ b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/project-reports.properties
@@ -1,17 +1,2 @@
-#
-# Copyright 2010 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-implementation-class=org.gradle.api.plugins.ProjectReportsPlugin
+implementation-class=org.gradle.api.plugins.ProjectReportsPlugin
 deprecated-by=project-report
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/war.properties b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/war.properties
index 2084bba..453ead8 100644
--- a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/war.properties
+++ b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/war.properties
@@ -1,16 +1 @@
-#
-# Copyright 2010 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-implementation-class=org.gradle.api.plugins.WarPlugin
+implementation-class=org.gradle.api.plugins.WarPlugin
diff --git a/subprojects/plugins/src/main/resources/org/gradle/api/internal/tasks/testing/junit/report/report.js b/subprojects/plugins/src/main/resources/org/gradle/api/internal/tasks/testing/junit/report/report.js
new file mode 100644
index 0000000..a4455e4
--- /dev/null
+++ b/subprojects/plugins/src/main/resources/org/gradle/api/internal/tasks/testing/junit/report/report.js
@@ -0,0 +1,101 @@
+var tabs = new Object();
+
+function initTabs() {
+    var container = document.getElementById('tabs');
+    tabs.tabs = findTabs(container);
+    tabs.titles = findTitles(tabs.tabs);
+    tabs.headers = findHeaders(container);
+    tabs.select = select;
+    tabs.deselectAll = deselectAll;
+    tabs.select(0);
+    return true;
+}
+
+window.onload = initTabs;
+
+function switchTab() {
+    var id = this.id.substr(1);
+    for (var i = 0; i < tabs.tabs.length; i++) {
+        if (tabs.tabs[i].id == id) {
+            tabs.select(i);
+            break;
+        }
+    }
+    return false;
+}
+
+function select(i) {
+    this.deselectAll();
+    changeElementClass(this.tabs[i], 'tab selected');
+    changeElementClass(this.headers[i], 'selected');
+    while (this.headers[i].firstChild) {
+        this.headers[i].removeChild(this.headers[i].firstChild);
+    }
+    var h2 = document.createElement('H2');
+    h2.appendChild(document.createTextNode(this.titles[i]));
+    this.headers[i].appendChild(h2);
+}
+
+function deselectAll() {
+    for (var i = 0; i < this.tabs.length; i++) {
+        changeElementClass(this.tabs[i], 'tab deselected');
+        changeElementClass(this.headers[i], 'deselected');
+        while (this.headers[i].firstChild) {
+            this.headers[i].removeChild(this.headers[i].firstChild);
+        }
+        var a = document.createElement('A');
+        a.setAttribute('id', 'ltab' + i);
+        a.setAttribute('href', '#tab' + i);
+        a.onclick = switchTab;
+        a.appendChild(document.createTextNode(this.titles[i]));
+        this.headers[i].appendChild(a);
+    }
+}
+
+function changeElementClass(element, classValue) {
+    if (element.getAttribute('className')) {
+        /* IE */
+        element.setAttribute('className', classValue)
+    } else {
+        element.setAttribute('class', classValue)
+    }
+}
+
+function findTabs(container) {
+    return findChildElements(container, 'DIV', 'tab');
+}
+
+function findHeaders(container) {
+    var owner = findChildElements(container, 'UL', 'tabLinks');
+    return findChildElements(owner[0], 'LI', null);
+}
+
+function findTitles(tabs) {
+    var titles = new Array();
+    for (var i = 0; i < tabs.length; i++) {
+        var tab = tabs[i];
+        var header = findChildElements(tab, 'H2', null)[0];
+        header.parentNode.removeChild(header);
+        if (header.innerText) {
+            titles.push(header.innerText)
+        } else {
+            titles.push(header.textContent)
+        }
+    }
+    return titles;
+}
+
+function findChildElements(container, name, targetClass) {
+    var elements = new Array();
+    var children = container.childNodes;
+    for (var i = 0; i < children.length; i++) {
+        var child = children.item(i);
+        if (child.nodeType == 1 && child.nodeName == name) {
+            if (targetClass && child.className.indexOf(targetClass) < 0) {
+                continue;
+            }
+            elements.push(child);
+        }
+    }
+    return elements;
+}
diff --git a/subprojects/plugins/src/main/resources/org/gradle/api/internal/tasks/testing/junit/report/style.css b/subprojects/plugins/src/main/resources/org/gradle/api/internal/tasks/testing/junit/report/style.css
new file mode 100644
index 0000000..90294dc
--- /dev/null
+++ b/subprojects/plugins/src/main/resources/org/gradle/api/internal/tasks/testing/junit/report/style.css
@@ -0,0 +1,212 @@
+
+body {
+    margin: 0;
+    padding: 0;
+    font-family: sans-serif;
+    font-size: 12pt;
+}
+
+body, a, a:visited {
+    color: #303030;
+}
+
+ul {
+    margin-left: 0;
+}
+
+#content {
+    padding-left: 50px;
+    padding-right: 50px;
+    padding-top: 30px;
+    padding-bottom: 30px;
+}
+
+#content h1 {
+    font-size: 160%;
+    margin-bottom: 10px;
+}
+
+#summary {
+    margin-top: 30px;
+    margin-bottom: 40px;
+}
+
+#summary table {
+    border-collapse: collapse;
+}
+
+#summary td {
+    vertical-align: top;
+}
+
+#footer {
+    margin-top: 100px;
+    font-size: 80%;
+}
+
+#footer, #footer a {
+    color: #a0a0a0;
+}
+
+.breadcrumbs, .breadcrumbs a {
+    color: #606060;
+}
+
+.infoBox {
+    width: 110px;
+    padding-top: 15px;
+    padding-bottom: 15px;
+    text-align: center;
+}
+
+.infoBox p {
+    margin: 0;
+}
+
+.counter, .percent {
+    font-size: 120%;
+    font-weight: bold;
+    margin-bottom: 8px;
+}
+
+#duration {
+    width: 125px;
+}
+
+#successRate, .summaryGroup {
+    border: solid 2px #d0d0d0;
+    -moz-border-radius: 10px;
+    border-radius: 10px;
+    behavior: url(css3-pie-1.0beta3.htc);
+}
+
+#successRate {
+    width: 140px;
+    margin-left: 35px;
+}
+
+#successRate .percent {
+    font-size: 180%;
+}
+
+.success, .success a {
+    color: #008000;
+}
+
+div.success, #successRate.success {
+    background-color: #bbd9bb;
+    border-color: #008000;
+}
+
+.failures, .failures a {
+    color: #b60808;
+}
+
+div.failures, #successRate.failures {
+    background-color: #ecdada;
+    border-color: #b60808;
+}
+
+h2 {
+    font-size: 120%;
+}
+
+ul.linkList {
+    padding-left: 0;
+}
+
+ul.linkList li {
+    list-style: none;
+    margin-bottom: 5px;
+}
+
+ul.tabLinks {
+    padding-left: 0;
+    padding-top: 10px;
+    padding-bottom: 10px;
+    overflow: hidden;
+}
+
+ul.tabLinks li {
+    float: left;
+    height: 100%;
+    list-style: none;
+    padding-left: 10px;
+    padding-right: 10px;
+    padding-top: 5px;
+    padding-bottom: 5px;
+    margin-bottom: 0;
+    -moz-border-radius: 7px;
+    border-radius: 7px;
+    margin-right: 25px;
+    border: solid 1px #d4d4d4;
+    background-color: #f0f0f0;
+    behavior: url(css3-pie-1.0beta3.htc);
+}
+
+ul.tabLinks li:hover {
+    background-color: #fafafa;
+}
+
+ul.tabLinks li.selected {
+    background-color: #c5f0f5;
+    border-color: #c5f0f5;
+}
+
+ul.tabLinks a {
+    font-size: 120%;
+    display: block;
+    outline: none;
+    text-decoration: none;
+    margin: 0;
+    padding: 0;
+}
+
+ul.tabLinks li:hover a {
+    text-decoration: underline;
+}
+
+ul.tabLinks li h2 {
+    margin: 0;
+    padding: 0;
+}
+
+div.tab {
+}
+
+div.selected {
+    display: block;
+}
+
+div.deselected {
+    display: none;
+}
+
+div.tab table {
+    width: 100%;
+    border-collapse: collapse;
+}
+
+div.tab th, div.tab table {
+    border-bottom: solid #d0d0d0 1px;
+}
+
+div.tab th {
+    text-align: left;
+}
+
+div.tab td {
+    padding-top: 5px;
+    padding-bottom: 5px;
+}
+
+div.tab pre {
+    font-size: 11pt;
+    padding-top: 10px;
+    padding-bottom: 10px;
+    padding-left: 10px;
+    padding-right: 10px;
+    background-color: #f7f7f7;
+    border: solid 1px #d0d0d0;
+    overflow-x: auto;
+}
diff --git a/subprojects/plugins/src/main/resources/org/gradle/api/tasks/application/unixStartScript.txt b/subprojects/plugins/src/main/resources/org/gradle/api/tasks/application/unixStartScript.txt
new file mode 100644
index 0000000..667cd5f
--- /dev/null
+++ b/subprojects/plugins/src/main/resources/org/gradle/api/tasks/application/unixStartScript.txt
@@ -0,0 +1,179 @@
+#!/bin/bash
+
+##############################################################################
+##
+##  ${applicationName} start up script for UN*X
+##
+##############################################################################
+
+# Uncomment those lines to set JVM options. ${optsEnvironmentVar} and JAVA_OPTS can be used together.
+# ${optsEnvironmentVar}="\$${optsEnvironmentVar} -Xmx512m"
+# JAVA_OPTS="\$JAVA_OPTS -Xmx512m"
+
+APP_NAME=${applicationName}
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "\$*"
+}
+
+die ( ) {
+    echo
+    echo "\$*"
+    echo
+    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
+
+# Attempt to set APP_HOME
+# Resolve links: \$0 may be a link
+PRG="\$0"
+# Need this for relative symlinks.
+while [ -h "\$PRG" ] ; do
+    ls=`ls -ld "\$PRG"`
+    link=`expr "\$ls" : '.*-> \\(.*\\)\$'`
+    if expr "\$link" : '/.*' > /dev/null; then
+        PRG="\$link"
+    else
+        PRG=`dirname "\$PRG"`"/\$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"\$PRG\"`/.."
+APP_HOME="`pwd -P`"
+cd "\$SAVED"
+
+CLASSPATH=$classpath
+
+# 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 "ERROR: JAVA_HOME is set to an invalid directory: \$JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+if [ -z "\$JAVA_HOME" ] ; then
+    warn "JAVA_HOME environment variable is not set"
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "\$cygwin" = "false" -a "\$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ \$? -eq 0 ] ; then
+        if [ "\$MAX_FD" = "maximum" -o "\$MAX_FD" = "max" ] ; then
+            MAX_FD="\$MAX_FD_LIMIT"
+        fi
+        ulimit -n \$MAX_FD
+        if [ \$? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: \$MAX_FD"
+        fi
+    else
+        warn "Could not query businessSystem maximum file descriptor limit: \$MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add APP_NAME to the JAVA_OPTS as -Xdock:name
+if \$darwin; then
+    JAVA_OPTS="\$JAVA_OPTS -Xdock:name=APP_NAME"
+# we may also want to set -Xdock:image
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if \$cygwin ; then
+    APP_HOME=`cygpath --path --mixed "\$APP_HOME"`
+    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
+
+exec "\$JAVACMD" \$JAVA_OPTS \$${optsEnvironmentVar} -classpath "\$CLASSPATH" ${mainClassName} "\$@"
diff --git a/subprojects/plugins/src/main/resources/org/gradle/api/tasks/application/windowsStartScript.txt b/subprojects/plugins/src/main/resources/org/gradle/api/tasks/application/windowsStartScript.txt
new file mode 100644
index 0000000..895f959
--- /dev/null
+++ b/subprojects/plugins/src/main/resources/org/gradle/api/tasks/application/windowsStartScript.txt
@@ -0,0 +1,82 @@
+ at if "%DEBUG%" == "" @echo off
+ at rem ##########################################################################
+ at rem
+ at rem  ${applicationName} startup script for Windows
+ at rem
+ 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. ${optsEnvironmentVar} and JAVA_OPTS can be used together.
+ at rem set ${optsEnvironmentVar}=%${optsEnvironmentVar}% -Xmx512m
+ at rem set JAVA_OPTS=%JAVA_OPTS% -Xmx512m
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.\
+
+set APP_HOME=%DIRNAME%..
+
+ at rem Find java.exe
+set JAVA_EXE=java.exe
+if not defined JAVA_HOME goto init
+
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is 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.
+echo.
+goto fail
+
+:init
+ at rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%eval[2+2]" == "4" goto 4NT_args
+
+: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
+
+
+set CLASSPATH=$classpath
+
+ at rem Execute ${applicationName}
+"%JAVA_EXE%" %JAVA_OPTS% %${optsEnvironmentVar}% -classpath "%CLASSPATH%" ${mainClassName} %CMD_LINE_ARGS%
+
+:end
+ at rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+if not "%OS%"=="Windows_NT" echo 1 > nul | choice /n /c:1
+
+rem Set variable ${exitEnvironmentVar} if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%${exitEnvironmentVar}%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/DefaultGroovySourceSetTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/DefaultGroovySourceSetTest.groovy
index 87dacc1..12db1af 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/DefaultGroovySourceSetTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/DefaultGroovySourceSetTest.groovy
@@ -17,11 +17,10 @@ package org.gradle.api.internal.tasks
 
 import org.gradle.api.internal.file.DefaultSourceDirectorySet
 import org.gradle.api.internal.file.FileResolver
-import org.gradle.api.internal.file.UnionFileTree
 import org.junit.Test
-import static org.gradle.util.Matchers.*
+import static org.gradle.util.Matchers.isEmpty
 import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
+import static org.junit.Assert.assertThat
 
 class DefaultGroovySourceSetTest {
     private final DefaultGroovySourceSet sourceSet = new DefaultGroovySourceSet("<set-display-name>", [resolve: {it as File}] as FileResolver)
@@ -31,19 +30,19 @@ class DefaultGroovySourceSetTest {
         assertThat(sourceSet.groovy, instanceOf(DefaultSourceDirectorySet))
         assertThat(sourceSet.groovy, isEmpty())
         assertThat(sourceSet.groovy.displayName, equalTo('<set-display-name> Groovy source'))
+        assertThat(sourceSet.groovy.filter.includes, equalTo(['**/*.groovy', '**/*.java'] as Set))
+        assertThat(sourceSet.groovy.filter.excludes, isEmpty())
 
-        assertThat(sourceSet.groovySourcePatterns.includes, equalTo(['**/*.groovy'] as Set))
-        assertThat(sourceSet.groovySourcePatterns.excludes, isEmpty())
-
-        assertThat(sourceSet.allGroovy, instanceOf(UnionFileTree))
         assertThat(sourceSet.allGroovy, isEmpty())
         assertThat(sourceSet.allGroovy.displayName, equalTo('<set-display-name> Groovy source'))
-        assertThat(sourceSet.allGroovy.sourceTrees, not(isEmpty()))
+        assertThat(sourceSet.allGroovy.source, hasItem(sourceSet.groovy))
+        assertThat(sourceSet.allGroovy.filter.includes, equalTo(['**/*.groovy'] as Set))
+        assertThat(sourceSet.allGroovy.filter.excludes, isEmpty())
     }
 
     @Test
     public void canConfigureGroovySource() {
         sourceSet.groovy { srcDir 'src/groovy' }
-        assertThat(sourceSet.groovy.srcDirs, equalTo([new File('src/groovy')] as Set))
+        assertThat(sourceSet.groovy.srcDirs, equalTo([new File('src/groovy').canonicalFile] as Set))
     }
 }
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/DefaultSourceSetTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/DefaultSourceSetTest.groovy
index 498d06b..dbd0438 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/DefaultSourceSetTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/DefaultSourceSetTest.groovy
@@ -15,15 +15,14 @@
  */
 package org.gradle.api.internal.tasks
 
+import org.gradle.api.Task
 import org.gradle.api.internal.file.DefaultSourceDirectorySet
 import org.gradle.api.internal.file.FileResolver
-import org.gradle.api.internal.file.UnionFileTree
 import org.gradle.api.tasks.SourceSet
 import org.junit.Test
-import static org.gradle.util.Matchers.*
+import static org.gradle.util.Matchers.isEmpty
 import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
-import org.gradle.api.Task
+import static org.junit.Assert.assertThat
 
 class DefaultSourceSetTest {
     private final FileResolver fileResolver = [resolve: {it as File}] as FileResolver
@@ -63,19 +62,20 @@ class DefaultSourceSetTest {
 
         assertThat(sourceSet.java.filter.includes, equalTo(['**/*.java'] as Set))
         assertThat(sourceSet.java.filter.excludes, isEmpty())
-        
 
-        assertThat(sourceSet.allJava, instanceOf(UnionFileTree))
+        assertThat(sourceSet.allJava, instanceOf(DefaultSourceDirectorySet))
         assertThat(sourceSet.allJava, isEmpty())
         assertThat(sourceSet.allJava.displayName, equalTo('set name Java source'))
         assertThat(sourceSet.allJava.toString(), equalTo('set name Java source'))
-        assertThat(sourceSet.allJava.sourceTrees, not(isEmpty()))
+        assertThat(sourceSet.allJava.source, hasItem(sourceSet.java))
+        assertThat(sourceSet.allJava.filter.includes, equalTo(['**/*.java'] as Set))
+        assertThat(sourceSet.allJava.filter.excludes, isEmpty())
 
-        assertThat(sourceSet.allSource, instanceOf(UnionFileTree))
+        assertThat(sourceSet.allSource, instanceOf(DefaultSourceDirectorySet))
         assertThat(sourceSet.allSource, isEmpty())
         assertThat(sourceSet.allSource.displayName, equalTo('set name source'))
         assertThat(sourceSet.allSource.toString(), equalTo('set name source'))
-        assertThat(sourceSet.allSource.sourceTrees, not(isEmpty()))
+        assertThat(sourceSet.allSource.source, hasItem(sourceSet.java))
     }
 
     @Test public void constructsTaskNamesUsingSourceSetName() {
@@ -105,13 +105,13 @@ class DefaultSourceSetTest {
     @Test public void canConfigureResources() {
         SourceSet sourceSet = new DefaultSourceSet('main', fileResolver, taskResolver)
         sourceSet.resources { srcDir 'src/resources' }
-        assertThat(sourceSet.resources.srcDirs, equalTo([new File('src/resources')] as Set))
+        assertThat(sourceSet.resources.srcDirs, equalTo([new File('src/resources').canonicalFile] as Set))
     }
     
     @Test public void canConfigureJavaSource() {
         SourceSet sourceSet = new DefaultSourceSet('main', fileResolver, taskResolver)
         sourceSet.java { srcDir 'src/java' }
-        assertThat(sourceSet.java.srcDirs, equalTo([new File('src/java')] as Set))
+        assertThat(sourceSet.java.srcDirs, equalTo([new File('src/java').canonicalFile] as Set))
     }
 
     @Test
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessorTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessorTest.groovy
index ffda8e6..e62d031 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessorTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessorTest.groovy
@@ -40,6 +40,8 @@ import org.gradle.logging.StandardOutputRedirector
 import org.junit.BeforeClass
 import junit.extensions.TestSetup
 import org.junit.After
+import org.gradle.api.tasks.testing.TestResult
+import junit.framework.TestSuite
 
 @RunWith(JMock.class)
 class JUnitTestClassProcessorTest {
@@ -81,6 +83,92 @@ class JUnitTestClassProcessorTest {
     }
 
     @Test
+    public void executesABrokenJUnit4TestClass() {
+        context.checking {
+            one(resultProcessor).started(withParam(notNullValue()), withParam(notNullValue()))
+            will { TestDescriptorInternal suite, TestStartEvent event ->
+                assertThat(suite.id, equalTo(1L))
+                assertThat(suite.name, equalTo(ABrokenTestClass.class.name))
+                assertThat(suite.className, equalTo(ABrokenTestClass.class.name))
+                assertThat(event.parentId, nullValue())
+            }
+            one(resultProcessor).started(withParam(notNullValue()), withParam(notNullValue()))
+            will { TestDescriptorInternal test, TestStartEvent event ->
+                assertThat(test.id, equalTo(2L))
+                assertThat(test.name, equalTo('broken'))
+                assertThat(test.className, equalTo(ABrokenTestClass.class.name))
+                assertThat(event.parentId, equalTo(1L))
+            }
+            one(resultProcessor).failure(2L, ABrokenTestClass.failure)
+            one(resultProcessor).completed(withParam(equalTo(2L)), withParam(notNullValue()))
+            will { id, TestCompleteEvent event ->
+                assertThat(event.resultType, nullValue())
+            }
+            one(resultProcessor).completed(withParam(equalTo(1L)), withParam(notNullValue()))
+            will { id, TestCompleteEvent event ->
+                assertThat(event.resultType, nullValue())
+            }
+        }
+
+        processor.startProcessing(resultProcessor);
+        processor.processTestClass(testClass(ABrokenTestClass.class));
+        processor.stop();
+    }
+
+    @Test
+    public void executesAJUnit4TestClassWithIgnoredTest() {
+        context.checking {
+            one(resultProcessor).started(withParam(notNullValue()), withParam(notNullValue()))
+            will { TestDescriptorInternal suite, TestStartEvent event ->
+                assertThat(suite.id, equalTo(1L))
+                assertThat(suite.name, equalTo(ATestClassWithIgnoredMethod.class.name))
+                assertThat(suite.className, equalTo(ATestClassWithIgnoredMethod.class.name))
+                assertThat(event.parentId, nullValue())
+            }
+            one(resultProcessor).started(withParam(notNullValue()), withParam(notNullValue()))
+            will { TestDescriptorInternal test, TestStartEvent event ->
+                assertThat(test.id, equalTo(2L))
+                assertThat(test.name, equalTo('ignored'))
+                assertThat(test.className, equalTo(ATestClassWithIgnoredMethod.class.name))
+                assertThat(event.parentId, equalTo(1L))
+            }
+            one(resultProcessor).completed(withParam(equalTo(2L)), withParam(notNullValue()))
+            will { id, TestCompleteEvent event ->
+                assertThat(event.resultType, equalTo(TestResult.ResultType.SKIPPED))
+            }
+            one(resultProcessor).completed(withParam(equalTo(1L)), withParam(notNullValue()))
+            will { id, TestCompleteEvent event ->
+                assertThat(event.resultType, nullValue())
+            }
+        }
+
+        processor.startProcessing(resultProcessor);
+        processor.processTestClass(testClass(ATestClassWithIgnoredMethod.class));
+        processor.stop();
+    }
+
+    @Test
+    public void executesAnIgnoredJUnit4TestClass() {
+        context.checking {
+            one(resultProcessor).started(withParam(notNullValue()), withParam(notNullValue()))
+            will { TestDescriptorInternal suite, TestStartEvent event ->
+                assertThat(suite.id, equalTo(1L))
+                assertThat(suite.name, equalTo(AnIgnoredTestClass.class.name))
+                assertThat(suite.className, equalTo(AnIgnoredTestClass.class.name))
+                assertThat(event.parentId, nullValue())
+            }
+            one(resultProcessor).completed(withParam(equalTo(1L)), withParam(notNullValue()))
+            will { id, TestCompleteEvent event ->
+                assertThat(event.resultType, nullValue())
+            }
+        }
+
+        processor.startProcessing(resultProcessor);
+        processor.processTestClass(testClass(AnIgnoredTestClass.class));
+        processor.stop();
+    }
+
+    @Test
     public void executesAJUnit3TestClass() {
         context.checking {
             one(resultProcessor).started(withParam(notNullValue()), withParam(notNullValue()))
@@ -113,6 +201,39 @@ class JUnitTestClassProcessorTest {
     }
 
     @Test
+    public void executesABrokenJUnit3TestClass() {
+        context.checking {
+            one(resultProcessor).started(withParam(notNullValue()), withParam(notNullValue()))
+            will { TestDescriptorInternal suite, TestStartEvent event ->
+                assertThat(suite.id, equalTo(1L))
+                assertThat(suite.name, equalTo(ABrokenJunit3TestClass.class.name))
+                assertThat(suite.className, equalTo(ABrokenJunit3TestClass.class.name))
+                assertThat(event.parentId, nullValue())
+            }
+            one(resultProcessor).started(withParam(notNullValue()), withParam(notNullValue()))
+            will { TestDescriptorInternal test, TestStartEvent event ->
+                assertThat(test.id, equalTo(2L))
+                assertThat(test.name, equalTo('testBroken'))
+                assertThat(test.className, equalTo(ABrokenJunit3TestClass.class.name))
+                assertThat(event.parentId, equalTo(1L))
+            }
+            one(resultProcessor).failure(2L, ABrokenJunit3TestClass.failure)
+            one(resultProcessor).completed(withParam(equalTo(2L)), withParam(notNullValue()))
+            will { id, TestCompleteEvent event ->
+                assertThat(event.resultType, nullValue())
+            }
+            one(resultProcessor).completed(withParam(equalTo(1L)), withParam(notNullValue()))
+            will { id, TestCompleteEvent event ->
+                assertThat(event.resultType, nullValue())
+            }
+        }
+
+        processor.startProcessing(resultProcessor);
+        processor.processTestClass(testClass(ABrokenJunit3TestClass.class));
+        processor.stop();
+    }
+
+    @Test
     public void executesMultipleTestClasses() {
         context.checking {
             one(resultProcessor).started(withParam(notNullValue()), withParam(notNullValue()))
@@ -258,7 +379,7 @@ class JUnitTestClassProcessorTest {
             one(resultProcessor).started(withParam(notNullValue()), withParam(notNullValue()))
             will { TestDescriptorInternal test ->
                 assertThat(test.id, equalTo(2L))
-                assertThat(test.name, equalTo('suite'))
+                assertThat(test.name, equalTo('initializationError'))
                 assertThat(test.className, equalTo(ATestClassWithBrokenSuiteMethod.class.name))
             }
             one(resultProcessor).failure(2L, ATestClassWithBrokenSuiteMethod.failure)
@@ -288,7 +409,7 @@ class JUnitTestClassProcessorTest {
             one(resultProcessor).started(withParam(notNullValue()), withParam(notNullValue()))
             will { TestDescriptorInternal test ->
                 assertThat(test.id, equalTo(2L))
-                assertThat(test.name, equalTo('classMethod'))
+                assertThat(test.name, equalTo(AJunit3TestClass.name))
                 assertThat(test.className, equalTo(ATestSetUpWithBrokenSetUp.class.name))
             }
             one(resultProcessor).failure(2L, ATestSetUpWithBrokenSetUp.failure)
@@ -393,9 +514,8 @@ class JUnitTestClassProcessorTest {
         processor.stop();
     }
 
-
     @Test
-    public void executesATestClassWithBrokenClassSetup() {
+    public void executesATestClassWithBrokenBeforeClassMethod() {
         context.checking {
             one(resultProcessor).started(withParam(notNullValue()), withParam(notNullValue()))
             will { TestDescriptorInternal suite ->
@@ -480,6 +600,38 @@ class JUnitTestClassProcessorTest {
         processor.stop();
     }
 
+    @Test
+    public void executesAJUnit3TestClassThatRenamesItself() {
+        context.checking {
+            one(resultProcessor).started(withParam(notNullValue()), withParam(notNullValue()))
+            will { TestDescriptorInternal suite, TestStartEvent event ->
+                assertThat(suite.id, equalTo(1L))
+                assertThat(suite.name, equalTo(AJunit3TestThatRenamesItself.class.name))
+                assertThat(suite.className, equalTo(AJunit3TestThatRenamesItself.class.name))
+                assertThat(event.parentId, nullValue())
+            }
+            one(resultProcessor).started(withParam(notNullValue()), withParam(notNullValue()))
+            will { TestDescriptorInternal test, TestStartEvent event ->
+                assertThat(test.id, equalTo(2L))
+                assertThat(test.name, equalTo('testOk'))
+                assertThat(test.className, equalTo(AJunit3TestThatRenamesItself.class.name))
+                assertThat(event.parentId, equalTo(1L))
+            }
+            one(resultProcessor).completed(withParam(equalTo(2L)), withParam(notNullValue()))
+            will { id, TestCompleteEvent event ->
+                assertThat(event.resultType, nullValue())
+            }
+            one(resultProcessor).completed(withParam(equalTo(1L)), withParam(notNullValue()))
+            will { id, TestCompleteEvent event ->
+                assertThat(event.resultType, nullValue())
+            }
+        }
+
+        processor.startProcessing(resultProcessor);
+        processor.processTestClass(testClass(AJunit3TestThatRenamesItself.class));
+        processor.stop();
+    }
+
     private TestClassRunInfo testClass(Class<?> type) {
         return testClass(type.name)
     }
@@ -498,12 +650,30 @@ public class ATestClass {
     @Test
     public void ok() {
     }
+}
 
+public class ATestClassWithIgnoredMethod {
     @Test @Ignore
     public void ignored() {
     }
 }
 
+ at Ignore
+public class AnIgnoredTestClass {
+    @Test
+    public void ignored() {
+    }
+}
+
+public class ABrokenTestClass {
+    static RuntimeException failure = new RuntimeException()
+
+    @Test
+    public void broken() {
+        throw failure.fillInStackTrace()
+    }
+}
+
 public class ATestClassWithBrokenConstructor {
     static RuntimeException failure = new RuntimeException()
 
@@ -566,6 +736,20 @@ public class AJunit3TestClass extends TestCase {
     }
 }
 
+public class AJunit3TestThatRenamesItself extends TestCase {
+    public void testOk() {
+        setName('another test')
+    }
+}
+
+public class ABrokenJunit3TestClass extends TestCase {
+    static RuntimeException failure = new RuntimeException()
+
+    public void testBroken() {
+        throw failure.fillInStackTrace()
+    }
+}
+
 public class ATestClassWithSuiteMethod {
     public static junit.framework.Test suite() {
         return new junit.framework.TestSuite(AJunit3TestClass.class, AJunit3TestClass.class);
@@ -583,9 +767,8 @@ public class ATestClassWithBrokenSuiteMethod {
 public class ATestSetUpWithBrokenSetUp extends TestSetup {
     static RuntimeException failure = new RuntimeException('broken')
 
-
     def ATestSetUpWithBrokenSetUp() {
-        super(null)
+        super(new TestSuite(AJunit3TestClass.class))
     }
 
     protected void setUp() {
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestFrameworkTest.java b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestFrameworkTest.java
index 67e353a..56381f1 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestFrameworkTest.java
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestFrameworkTest.java
@@ -16,24 +16,26 @@
 
 package org.gradle.api.internal.tasks.testing.junit;
 
+import org.gradle.api.AntBuilder;
 import org.gradle.api.internal.project.ServiceRegistry;
 import org.gradle.api.internal.tasks.testing.AbstractTestFrameworkTest;
 import org.gradle.api.internal.tasks.testing.TestClassProcessor;
+import org.gradle.api.internal.tasks.testing.junit.report.TestReporter;
 import org.gradle.api.tasks.testing.junit.JUnitOptions;
 import org.gradle.util.IdGenerator;
 import org.jmock.Expectations;
 import org.junit.Before;
 
 import static junit.framework.Assert.assertNotNull;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertThat;
 
 /**
  * @author Tom Eyckmans
  */
 public class JUnitTestFrameworkTest extends AbstractTestFrameworkTest {
     private JUnitTestFramework jUnitTestFramework;
-    private AntJUnitReport antJUnitReportMock;
+    private TestReporter reporterMock;
     private JUnitOptions jUnitOptionsMock;
     private IdGenerator<?> idGenerator;
     private ServiceRegistry serviceRegistry;
@@ -42,7 +44,7 @@ public class JUnitTestFrameworkTest extends AbstractTestFrameworkTest {
     public void setUp() throws Exception {
         super.setUp();
 
-        antJUnitReportMock = context.mock(AntJUnitReport.class);
+        reporterMock = context.mock(TestReporter.class);
         jUnitOptionsMock = context.mock(JUnitOptions.class);
         idGenerator = context.mock(IdGenerator.class);
         serviceRegistry = context.mock(ServiceRegistry.class);
@@ -50,6 +52,7 @@ public class JUnitTestFrameworkTest extends AbstractTestFrameworkTest {
         context.checking(new Expectations(){{
             allowing(testMock).getTestClassesDir(); will(returnValue(testClassesDir));
             allowing(testMock).getClasspath(); will(returnValue(classpathMock));
+            allowing(testMock).getAnt(); will(returnValue(context.mock(AntBuilder.class)));
         }});
     }
 
@@ -59,7 +62,7 @@ public class JUnitTestFrameworkTest extends AbstractTestFrameworkTest {
         setMocks();
 
         assertNotNull(jUnitTestFramework.getOptions());
-        assertNotNull(jUnitTestFramework.getAntJUnitReport());
+        assertNotNull(jUnitTestFramework.getReporter());
     }
 
     @org.junit.Test
@@ -84,12 +87,10 @@ public class JUnitTestFrameworkTest extends AbstractTestFrameworkTest {
         context.checking(new Expectations() {{
             one(testMock).getTestResultsDir(); will(returnValue(testResultsDir));
             one(testMock).getTestReportDir(); will(returnValue(testReportDir));
-            one(projectMock).getAnt(); will(returnValue(antBuilderMock));
             one(testMock).isTestReport(); will(returnValue(true));
-            one(antJUnitReportMock).execute(
-                    testResultsDir, testReportDir,
-                    antBuilderMock
-            );
+            one(reporterMock).setTestReportDir(testReportDir);
+            one(reporterMock).setTestResultsDir(testResultsDir);
+            one(reporterMock).generateReport();
         }});
 
         jUnitTestFramework.report();
@@ -108,7 +109,7 @@ public class JUnitTestFrameworkTest extends AbstractTestFrameworkTest {
     }
 
     private void setMocks() {
-        jUnitTestFramework.setAntJUnitReport(antJUnitReportMock);
+        jUnitTestFramework.setReporter(reporterMock);
         jUnitTestFramework.setOptions(jUnitOptionsMock);
     }
 }
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/AllTestResultsTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/AllTestResultsTest.groovy
new file mode 100644
index 0000000..fc3b8ce
--- /dev/null
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/AllTestResultsTest.groovy
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report
+
+import spock.lang.Specification
+
+class AllTestResultsTest extends Specification {
+    final AllTestResults results = new AllTestResults()
+
+    def addsTest() {
+        when:
+        def test = results.addTest('org.gradle.Test', 'test', 90)
+
+        then:
+        test.name == 'test'
+        test.classResults.name == 'org.gradle.Test'
+        test.classResults.packageResults.name == 'org.gradle'
+        results.packages.contains(test.classResults.packageResults)
+    }
+    
+    def addsTestInDefaultPackage() {
+        when:
+        def test = results.addTest('Test', 'test', 90)
+
+        then:
+        test.name == 'test'
+        test.classResults.name == 'Test'
+        test.classResults.packageResults.name == 'default-package'
+        results.packages.contains(test.classResults.packageResults)
+    }
+}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/ClassTestResultsTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/ClassTestResultsTest.groovy
new file mode 100644
index 0000000..e546714
--- /dev/null
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/ClassTestResultsTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report
+
+import spock.lang.Specification
+
+class ClassTestResultsTest extends Specification {
+    def determinesSimpleName() {
+        expect:
+        new ClassTestResults('org.gradle.Test', null).simpleName == 'Test'
+        new ClassTestResults('Test', null).simpleName == 'Test'
+    }
+}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/CompositeTestResultsTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/CompositeTestResultsTest.groovy
new file mode 100644
index 0000000..b641fed
--- /dev/null
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/CompositeTestResultsTest.groovy
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report
+
+import spock.lang.Specification
+
+class CompositeTestResultsTest extends Specification {
+    final CompositeTestResults results = new CompositeTestResults(null) {
+        @Override
+        String getTitle() {
+            throw new UnsupportedOperationException()
+        }
+    }
+
+    def formatsSuccessRateWhenNoTests() {
+        expect:
+        results.successRate == null
+        results.formattedSuccessRate == "-"
+    }
+
+    def formatsSuccessRateWhenAllTestsPass() {
+        results.addTest(test())
+
+        expect:
+        results.successRate == 100
+        results.formattedSuccessRate == '100%'
+    }
+
+    def formatsSuccessRateWhenSomeTestsFail() {
+        def failed = results.addTest(test())
+        results.failed(failed)
+        results.addTest(test())
+        results.addTest(test())
+
+        expect:
+        results.successRate == 66
+        results.formattedSuccessRate == '66%'
+    }
+
+    def formatsDurationWhenNoTests() {
+        expect:
+        results.formattedDuration == '-'
+    }
+
+    def formatsDurationWhenTests() {
+        results.addTest(test())
+
+        expect:
+        results.formattedDuration == '0.045s'
+    }
+
+    private TestResult test() {
+        return new TestResult('test', 45, null)
+    }
+}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/DefaultTestReportTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/DefaultTestReportTest.groovy
new file mode 100644
index 0000000..2327d5a
--- /dev/null
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/DefaultTestReportTest.groovy
@@ -0,0 +1,376 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report
+
+import org.gradle.util.TemporaryFolder
+import org.gradle.util.TestFile
+import org.junit.Rule
+import spock.lang.Specification
+import org.cyberneko.html.parsers.SAXParser
+
+class DefaultTestReportTest extends Specification {
+    @Rule public final TemporaryFolder tmpDir = new TemporaryFolder()
+    final DefaultTestReport report = new DefaultTestReport()
+    final TestFile reportDir = tmpDir.file('report')
+    final TestFile resultsDir = tmpDir.file('results')
+    final TestFile indexFile = reportDir.file('index.html')
+
+    def setup() {
+        report.testReportDir = reportDir
+        report.testResultsDir = resultsDir
+    }
+
+    def generatesReportWhenResultsDirectoryDoesNotExist() {
+        when:
+        report.generateReport()
+
+        then:
+        def index = results(indexFile)
+        index.assertHasTests(0)
+    }
+
+    def generatesReportWhenThereAreNoTestResults() {
+        resultsDir.mkdir()
+
+        when:
+        report.generateReport()
+
+        then:
+        def index = results(indexFile)
+        index.assertHasTests(0)
+        index.assertHasFailures(0)
+        index.assertHasNoDuration()
+        index.assertHasNoSuccessRate()
+        index.assertHasNoNavLinks()
+    }
+
+    def generatesReportWhichIncludesContentsOfEachTestResultFile() {
+        resultsDir.file('TEST-someClass.xml') << '''
+<testsuite name="org.gradle.Test">
+    <testcase classname="org.gradle.Test" name="test1" time="0.0010"/>
+    <testcase classname="org.gradle.Test" name="test2" time="0.0040"/>
+    <system-out>this is
+standard output</system-out>
+    <system-err>this is
+standard error</system-err>
+</testsuite>
+'''
+        resultsDir.file('TEST-someOtherClass.xml') << '''
+<testsuite name="org.gradle.Test2">
+    <testcase classname="org.gradle.Test2" name="test1" time="102.0010"/>
+    <testcase classname="org.gradle.sub.Test" name="test1" time="12.9"/>
+</testsuite>
+'''
+
+        when:
+        report.generateReport()
+
+        then:
+        def index = results(indexFile)
+        index.assertHasTests(4)
+        index.assertHasFailures(0)
+        index.assertHasSuccessRate(100)
+        index.assertHasDuration("1m54.91s")
+        index.assertHasLinkTo('org.gradle')
+        index.assertHasLinkTo('org.gradle.sub')
+        index.assertHasLinkTo('org.gradle.Test', 'org.gradle.Test')
+
+        reportDir.file("style.css").assertIsFile()
+
+        def packageFile = results(reportDir.file('org.gradle.html'))
+        packageFile.assertHasTests(3)
+        packageFile.assertHasFailures(0)
+        packageFile.assertHasSuccessRate(100)
+        packageFile.assertHasDuration("1m42.01s")
+        packageFile.assertHasLinkTo('org.gradle.Test', 'Test')
+        packageFile.assertHasLinkTo('org.gradle.Test2', 'Test2')
+
+        def testClassFile = results(reportDir.file('org.gradle.Test.html'))
+        testClassFile.assertHasTests(2)
+        testClassFile.assertHasFailures(0)
+        testClassFile.assertHasSuccessRate(100)
+        testClassFile.assertHasDuration("0.005s")
+        testClassFile.assertHasTest('test1')
+        testClassFile.assertHasTest('test2')
+        testClassFile.assertHasStandardOutput('this is\nstandard output')
+        testClassFile.assertHasStandardError('this is\nstandard error')
+    }
+
+    def generatesReportWhenThereAreFailures() {
+        resultsDir.file('TEST-someClass.xml') << '''
+<testsuite>
+    <testcase classname="org.gradle.Test" name="test1" time="0"><failure message="something failed">this is the failure
+at someClass
+</failure></testcase>
+    <testcase classname="org.gradle.Test" name="test2" time="0"><failure message="a multi-line
+message">this is a failure.</failure></testcase>
+    <testcase classname="org.gradle.Test2" name="test1" time="0"/>
+    <testcase classname="org.gradle.sub.Test" name="test1" time="0"/>
+</testsuite>
+'''
+
+        when:
+        report.generateReport()
+
+        then:
+        def index = results(indexFile)
+        index.assertHasTests(4)
+        index.assertHasFailures(2)
+        index.assertHasSuccessRate(50)
+        index.assertHasFailedTest('org.gradle.Test', 'test1')
+
+        def packageFile = results(reportDir.file('org.gradle.html'))
+        packageFile.assertHasTests(3)
+        packageFile.assertHasFailures(2)
+        packageFile.assertHasSuccessRate(33)
+        packageFile.assertHasFailedTest('org.gradle.Test', 'test1')
+
+        def testClassFile = results(reportDir.file('org.gradle.Test.html'))
+        testClassFile.assertHasTests(2)
+        testClassFile.assertHasFailures(2)
+        testClassFile.assertHasSuccessRate(0)
+        testClassFile.assertHasTest('test1')
+        testClassFile.assertHasFailure('test1', 'this is the failure\nat someClass\n')
+        testClassFile.assertHasTest('test2')
+        testClassFile.assertHasFailure('test2', 'this is a failure.')
+    }
+
+    def generatesReportWhenThereAreIgnoredTests() {
+        resultsDir.file('TEST-someClass.xml') << '''
+<testsuite>
+    <ignored-testcase classname="org.gradle.Test" name="test1"/>
+</testsuite>
+'''
+
+        when:
+        report.generateReport()
+
+        then:
+        def index = results(indexFile)
+        index.assertHasTests(1)
+        index.assertHasFailures(0)
+        index.assertHasSuccessRate(100)
+
+        def packageFile = results(reportDir.file('org.gradle.html'))
+        packageFile.assertHasTests(1)
+        packageFile.assertHasFailures(0)
+        packageFile.assertHasSuccessRate(100)
+
+        def testClassFile = results(reportDir.file('org.gradle.Test.html'))
+        testClassFile.assertHasTests(1)
+        testClassFile.assertHasFailures(0)
+        testClassFile.assertHasSuccessRate(100)
+        testClassFile.assertHasTest('test1')
+        testClassFile.assertTestIgnored('test1')
+    }
+
+    def reportsOnClassesInDefaultPackage() {
+        resultsDir.file('TEST-someClass.xml') << '''
+<testsuite name="Test">
+    <testcase classname="Test" name="test1" time="0">
+    </testcase>
+</testsuite>
+'''
+
+        when:
+        report.generateReport()
+
+        then:
+        def index = results(indexFile)
+        index.assertHasLinkTo('default-package')
+        index.assertHasLinkTo('Test')
+
+        def packageFile = results(reportDir.file('default-package.html'))
+        packageFile.assertHasLinkTo('Test')
+    }
+
+    def escapesHtmlContentInReport() {
+        resultsDir.file('TEST-someClass.xml') << '''
+<testsuite name="org.gradle.Test">
+    <testcase classname="org.gradle.Test" name="test1 < test2" time="0">
+        <failure message="something failed"><a failure></failure>
+    </testcase>
+    <system-out></html> & </system-out>
+    <system-err></div> & </system-err>
+</testsuite>
+'''
+
+        when:
+        report.generateReport()
+
+        then:
+        def testClassFile = results(reportDir.file('org.gradle.Test.html'))
+        testClassFile.assertHasTest('test1 < test2')
+        testClassFile.assertHasFailure('test1 < test2', '<a failure>')
+        testClassFile.assertHasStandardOutput('</html> & ')
+        testClassFile.assertHasStandardError('</div> & ')
+    }
+
+    def encodesUnicodeCharactersInReport() {
+        resultsDir.file('TEST-someClass.xml') << '''
+<testsuite name="org.gradle.Test">
+    <testcase classname="org.gradle.Test" name="&#x0107;" time="0"/>
+    <system-out>out:&#x0256;</system-out>
+    <system-err>err:&#x0102;</system-err>
+</testsuite>
+'''
+
+        when:
+        report.generateReport()
+
+        then:
+        def testClassFile = results(reportDir.file('org.gradle.Test.html'))
+        testClassFile.assertHasTest('\u0107')
+        testClassFile.assertHasStandardOutput('out:\u0256')
+        testClassFile.assertHasStandardError('err:\u0102')
+    }
+
+    def ignoresFilesWhichAreNotResultFiles() {
+        resultsDir.file('TEST-someClass.xml') << '''
+<testsuite name="org.gradle.Test">
+    <testcase classname="org.gradle.Test" name="test1" time="0"></testcase>
+</testsuite>
+'''
+        resultsDir.file('TESTS-broken.xml') << 'broken'
+
+        when:
+        report.generateReport()
+
+        then:
+        results(indexFile).assertHasTests(1)
+    }
+
+    def results(TestFile file) {
+        return new TestResultsFixture(file)
+    }
+}
+
+class TestResultsFixture {
+    final TestFile file
+    Node content
+
+    TestResultsFixture(TestFile file) {
+        this.file = file
+        file.assertIsFile()
+        def text = file.getText('utf-8').readLines()
+        def withoutDocType = text.subList(1, text.size()).join('\n')
+        content = new XmlParser(new SAXParser()).parseText(withoutDocType)
+    }
+
+    void assertHasTests(int tests) {
+        Node testDiv = content.depthFirst().find { it.'@id' == 'tests' }
+        assert testDiv != null
+        Node counter = testDiv.DIV.find { it.'@class' == 'counter' }
+        assert counter != null
+        assert counter.text() == tests as String
+    }
+
+    void assertHasFailures(int tests) {
+        Node testDiv = content.depthFirst().find { it.'@id' == 'failures' }
+        assert testDiv != null
+        Node counter = testDiv.DIV.find { it.'@class' == 'counter' }
+        assert counter != null
+        assert counter.text() == tests as String
+    }
+
+    void assertHasDuration(String duration) {
+        Node testDiv = content.depthFirst().find { it.'@id' == 'duration' }
+        assert testDiv != null
+        Node counter = testDiv.DIV.find { it.'@class' == 'counter' }
+        assert counter != null
+        assert counter.text() == duration
+    }
+
+    void assertHasNoDuration() {
+        Node testDiv = content.depthFirst().find { it.'@id' == 'duration' }
+        assert testDiv != null
+        Node counter = testDiv.DIV.find { it.'@class' == 'counter' }
+        assert counter != null
+        assert counter.text() == '-'
+    }
+    
+    void assertHasSuccessRate(int rate) {
+        Node testDiv = content.depthFirst().find { it.'@id' == 'successRate' }
+        assert testDiv != null
+        Node counter = testDiv.DIV.find { it.'@class' == 'percent' }
+        assert counter != null
+        assert counter.text() == "${rate}%"
+    }
+
+    void assertHasNoSuccessRate() {
+        Node testDiv = content.depthFirst().find { it.'@id' == 'successRate' }
+        assert testDiv != null
+        Node counter = testDiv.DIV.find { it.'@class' == 'percent' }
+        assert counter != null
+        assert counter.text() == '-'
+    }
+
+    void assertHasNoNavLinks() {
+        assert findTab('Packages') == null
+    }
+
+    void assertHasLinkTo(String target, String display = target) {
+        assert content.depthFirst().find { it.name() == 'A' && it.'@href' == "${target}.html" && it.text() == display }
+    }
+
+    void assertHasFailedTest(String className, String testName) {
+        def tab = findTab('Failed tests')
+        assert tab != null
+        assert tab.depthFirst().find { it.name() == 'A' && it.'@href' == "${className}.html#${testName}" && it.text() == testName }
+    }
+
+    void assertHasTest(String testName) {
+        assert findTestDetails(testName)
+    }
+
+    void assertTestIgnored(String testName) {
+        def row = findTestDetails(testName)
+        assert row.TD[2].text() == 'ignored'
+    }
+
+    void assertHasFailure(String testName, String stackTrace) {
+        def detailsRow = findTestDetails(testName)
+        assert detailsRow.TD[2].text() == 'failed'
+
+        def tab = findTab('Failed tests')
+        assert tab != null
+        def pre = tab.depthFirst().findAll { it.name() == 'PRE' }
+        assert pre.find { it.text() == stackTrace.trim() }
+    }
+
+    private def findTestDetails(String testName) {
+        def tab = findTab('Tests')
+        def anchor = tab.depthFirst().find { it.name() == 'TD' && it.text() == testName }
+        return anchor?.parent()
+    }
+
+    void assertHasStandardOutput(String stdout) {
+        def tab = findTab('Standard output')
+        assert tab != null
+        assert tab.PRE[0].text() == stdout.trim()
+    }
+
+    void assertHasStandardError(String stderr) {
+        def tab = findTab('Standard error')
+        assert tab != null
+        assert tab.PRE[0].text() == stderr.trim()
+    }
+
+    private def findTab(String title) {
+        def tab = content.depthFirst().find { it.name() == 'DIV' && it.'@class' == 'tab' && it.H2[0].text() == title }
+        return tab
+    }
+}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/LocaleSafeDecimalFormatTest.java b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/LocaleSafeDecimalFormatTest.java
new file mode 100644
index 0000000..17d0caa
--- /dev/null
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/LocaleSafeDecimalFormatTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report;
+
+import org.junit.After;
+import org.junit.Test;
+
+import java.math.BigDecimal;
+import java.util.Locale;
+
+import static junit.framework.Assert.assertEquals;
+
+/**
+ * @author Szczepan Faber, @date: 09.03.11
+ */
+public class LocaleSafeDecimalFormatTest {
+
+    Locale defaultLocale = Locale.getDefault();
+
+    @After
+    public void revertLocalToDefault() {
+        Locale.setDefault(defaultLocale);
+    }
+
+    @Test
+    public void parsesCommaEvenInRareLocales() throws Exception {
+        //given
+        Locale usesCommaAsDecimalSeparator = new Locale("PL", "pl", "");
+        Locale.setDefault(usesCommaAsDecimalSeparator);
+
+        //when
+        BigDecimal result = new LocaleSafeDecimalFormat().parse("1.05");
+
+        //then
+        assertEquals(1.05, result.doubleValue());
+    }
+}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/TestResultModelTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/TestResultModelTest.groovy
new file mode 100644
index 0000000..81fe530
--- /dev/null
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/TestResultModelTest.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report
+
+import spock.lang.Specification
+
+class TestResultModelTest extends Specification {
+    def formatsShortDurations() {
+        expect:
+        test(0).formattedDuration == '0s'
+        test(7).formattedDuration == '0.007s'
+        test(1200).formattedDuration == '1.200s'
+    }
+
+    def formatsLongDuration() {
+        expect:
+        test(60000).formattedDuration == '1m0.00s'
+        test(72301).formattedDuration == '1m12.30s'
+        test(72305).formattedDuration == '1m12.31s'
+        test(60 * 60 * 1000).formattedDuration == '1h0m0.00s'
+        test(24 * 60 * 60 * 1000).formattedDuration == '1d0h0m0.00s'
+    }
+
+    def test(long duration) {
+        return new TestResult('test', duration, null)
+    }
+}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/TestResultTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/TestResultTest.groovy
new file mode 100644
index 0000000..f8bf64d
--- /dev/null
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/TestResultTest.groovy
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report
+
+import spock.lang.Specification
+
+class TestResultTest extends Specification {
+    def canOrderResultsByClassNameAndTestName() {
+        ClassTestResults class1 = Mock()
+        _ * class1.name >> 'name'
+        ClassTestResults class2 = Mock()
+        _ * class2.name >> 'a'
+        ClassTestResults class3 = Mock()
+        _ * class3.name >> 'z'
+
+        TestResult result = new TestResult('name', 0, class1)
+        TestResult smallerClass = new TestResult('name', 0, class2)
+        TestResult largerClass = new TestResult('name', 0, class3)
+        TestResult smallerName = new TestResult('a', 0, class1)
+        TestResult largerName = new TestResult('z', 0, class1)
+
+        expect:
+        [result, largerName, smallerClass, largerClass, smallerName].sort() == [smallerClass, smallerName, result, largerName, largerClass]
+    }
+
+    def doesNotDiscardDuplicatesWhenSorting() {
+        ClassTestResults class1 = Mock()
+        _ * class1.name >> 'name'
+
+        TestResult result = new TestResult('name', 0, class1)
+        TestResult equalResult = new TestResult('name', 0, class1)
+
+        expect:
+        def r = [result, equalResult] as SortedSet
+        r.size() == 2
+    }
+}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/results/TestLoggerTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/results/TestLoggerTest.groovy
index 30d4f2c..511bd38 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/results/TestLoggerTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/results/TestLoggerTest.groovy
@@ -37,7 +37,8 @@ class TestLoggerTest extends Specification {
         logger.beforeSuite(rootSuite)
 
         then:
-        1 * factory.start(TestLogger.name) >> progressLogger
+        1 * factory.newOperation(TestLogger) >> progressLogger
+        1 * progressLogger.started()
 
         when:
         logger.afterSuite(rootSuite, result())
@@ -50,7 +51,7 @@ class TestLoggerTest extends Specification {
         TestDescriptor test1 = test()
         TestDescriptor test2 = test()
 
-        1 * factory.start(TestLogger.name) >> progressLogger
+        1 * factory.newOperation(TestLogger) >> progressLogger
         logger.beforeSuite(rootSuite)
 
         when:
@@ -76,7 +77,7 @@ class TestLoggerTest extends Specification {
         TestDescriptor test1 = test()
         TestDescriptor test2 = test()
 
-        1 * factory.start(TestLogger.name) >> progressLogger
+        1 * factory.newOperation(TestLogger) >> progressLogger
         logger.beforeSuite(rootSuite)
 
         when:
@@ -102,7 +103,7 @@ class TestLoggerTest extends Specification {
     def ignoresSuitesOtherThanTheRootSuite() {
         TestDescriptor suite = suite()
 
-        1 * factory.start(TestLogger.name) >> progressLogger
+        1 * factory.newOperation(TestLogger) >> progressLogger
         logger.beforeSuite(rootSuite)
 
         when:
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/ApplicationPluginTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/ApplicationPluginTest.groovy
new file mode 100644
index 0000000..8a0acb7
--- /dev/null
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/ApplicationPluginTest.groovy
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.Project
+import org.gradle.api.tasks.application.CreateStartScripts
+import org.gradle.api.tasks.Copy
+import org.gradle.api.tasks.JavaExec
+import org.gradle.api.tasks.SourceSet
+import org.gradle.api.tasks.bundling.Zip
+import org.gradle.util.HelperUtil
+import org.gradle.util.Matchers
+import spock.lang.Specification
+import org.gradle.api.tasks.Sync
+
+class ApplicationPluginTest extends Specification {
+    private final Project project = HelperUtil.createRootProject();
+    private final ApplicationPlugin plugin = new ApplicationPlugin();
+
+    public void appliesJavaPluginAndAddsConventionObjectWithDefaultValues() {
+        when:
+        plugin.apply(project)
+
+        then:
+        project.plugins.hasPlugin(JavaPlugin.class)
+        project.convention.getPlugin(ApplicationPluginConvention.class) != null
+        project.applicationName == project.name
+        project.mainClassName == null
+    }
+
+    public void addsRunTasksToProject() {
+        when:
+        plugin.apply(project)
+
+        then:
+        def task = project.tasks[ApplicationPlugin.TASK_RUN_NAME]
+        task instanceof JavaExec
+        task.classpath == project.sourceSets[SourceSet.MAIN_SOURCE_SET_NAME].runtimeClasspath
+        task Matchers.dependsOn('classes')
+    }
+
+    public void addsCreateStartScriptsTaskToProject() {
+        when:
+        plugin.apply(project)
+
+        then:
+        def task = project.tasks[ApplicationPlugin.TASK_START_SCRIPTS_NAME]
+        task instanceof CreateStartScripts
+        task.applicationName == project.applicationName
+        task.outputDir == project.file('build/scripts')
+    }
+
+    public void addsInstallTaskToProjectWithDefaultTarget() {
+        when:
+        plugin.apply(project)
+
+        then:
+        def task = project.tasks[ApplicationPlugin.TASK_INSTALL_NAME]
+        task instanceof Sync
+        task.destinationDir == project.file("build/install/${project.applicationName}")
+    }
+
+    public void addsDistZipTaskToProject() {
+        when:
+        plugin.apply(project)
+
+        then:
+        def task = project.tasks[ApplicationPlugin.TASK_DIST_ZIP_NAME]
+        task instanceof Zip
+        task.archiveName == "${project.applicationName}.zip"
+    }
+
+    public void canChangeApplicationName() {
+        when:
+        plugin.apply(project)
+        project.applicationName = "SuperApp";
+
+        then:
+        def startScriptsTask = project.tasks[ApplicationPlugin.TASK_START_SCRIPTS_NAME]
+        startScriptsTask.applicationName == 'SuperApp'
+
+        def installTest = project.tasks[ApplicationPlugin.TASK_INSTALL_NAME]
+        installTest.destinationDir == project.file("build/install/SuperApp")
+
+        def distZipTask = project.tasks[ApplicationPlugin.TASK_DIST_ZIP_NAME]
+        distZipTask.archiveName == "SuperApp.zip"
+    }
+    
+    public void setMainClassNameSetsMainInRunTask() {
+        when:
+        plugin.apply(project)
+        project.mainClassName = "Acme";
+
+        then:
+        def run = project.tasks[ApplicationPlugin.TASK_RUN_NAME]
+        run.main == "Acme"
+    }
+
+    public void setMainClassNameSetsMainClassNameInCreateStartScriptsTask() {
+        when:
+        plugin.apply(project);
+        project.mainClassName = "Acme"
+
+        then:
+        def startScripts = project.tasks[ApplicationPlugin.TASK_START_SCRIPTS_NAME]
+        startScripts.mainClassName == "Acme"
+    }
+}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/BasePluginTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/BasePluginTest.groovy
index f8158ed..d223d71 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/BasePluginTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/BasePluginTest.groovy
@@ -105,6 +105,18 @@ class BasePluginTest {
         assertThat(cleanTest.delete, equalTo([test.outputs.files] as Set))
     }
 
+    @Test public void cleanRuleIsCaseSensitive() {
+        plugin.apply(project)
+
+        project.task('testTask')
+        project.task('12')
+
+        assertThat(project.tasks.findByName('cleantestTask'), nullValue())
+        assertThat(project.tasks.findByName('cleanTesttask'), nullValue())
+        assertThat(project.tasks.findByName('cleanTestTask'), instanceOf(Delete.class))
+        assertThat(project.tasks.findByName('clean12'), instanceOf(Delete.class))
+    }
+
     @Test public void appliesMappingsForArchiveTasks() {
         plugin.apply(project)
 
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaPluginTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaPluginTest.groovy
index 33203f2..ac3d071 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaPluginTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaPluginTest.groovy
@@ -38,6 +38,7 @@ import static org.gradle.util.WrapUtil.toLinkedSet
 import static org.gradle.util.WrapUtil.toSet
 import static org.hamcrest.Matchers.*
 import static org.junit.Assert.*
+import static org.gradle.util.Matchers.sameCollection
 
 /**
  * @author Hans Dockter
@@ -187,8 +188,7 @@ class JavaPluginTest {
         assertThat(task, instanceOf(Javadoc))
         assertThat(task, dependsOn(JavaPlugin.CLASSES_TASK_NAME))
         assertThat(task.source.files, equalTo(project.sourceSets.main.allJava.files))
-        assertThat(task.classpath.sourceCollections, hasItem(project.sourceSets.main.classes))
-        assertThat(task.classpath.sourceCollections, hasItem(project.sourceSets.main.compileClasspath))
+        assertThat(task.classpath, sameCollection(project.files(project.sourceSets.main.classes, project.sourceSets.main.compileClasspath)))
         assertThat(task.destinationDir, equalTo(project.file("$project.docsDir/javadoc")))
         assertThat(task.title, equalTo(project.apiDocTitle))
 
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/application/CreateStartScriptsTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/application/CreateStartScriptsTest.groovy
new file mode 100644
index 0000000..9a7912a
--- /dev/null
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/application/CreateStartScriptsTest.groovy
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.application
+
+import spock.lang.Specification
+import org.gradle.util.HelperUtil
+
+class CreateStartScriptsTest extends Specification {
+    final CreateStartScripts task = HelperUtil.createTask(CreateStartScripts.class)
+
+    def scriptNameDefaultsToApplicationName() {
+        task.outputDir = new File('output')
+
+        when:
+        task.applicationName = "myApp"
+
+        then:
+        task.unixScript == new File(task.outputDir, 'myApp')
+        task.windowsScript == new File(task.outputDir, 'myApp.bat')
+    }
+    
+    def optsEnvironmentVariableNameDefaultsToApplicationName() {
+        when:
+        task.applicationName = null
+
+        then:
+        task.optsEnvironmentVar == null
+
+        when:
+        task.applicationName = "myApp"
+
+        then:
+        task.optsEnvironmentVar == 'MY_APP_OPTS'
+
+        when:
+        task.optsEnvironmentVar = 'APP_OPTS'
+
+        then:
+        task.optsEnvironmentVar == 'APP_OPTS'
+    }
+
+    def exitEnvironmentVariableNameDefaultsToApplicationName() {
+        when:
+        task.applicationName = null
+
+        then:
+        task.exitEnvironmentVar == null
+
+        when:
+        task.applicationName = "myApp"
+
+        then:
+        task.exitEnvironmentVar == 'MY_APP_EXIT_CONSOLE'
+
+        when:
+        task.exitEnvironmentVar = 'APP_EXIT'
+
+        then:
+        task.exitEnvironmentVar == 'APP_EXIT'
+    }
+}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/AbstractCompileTest.java b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/AbstractCompileTest.java
index 814ebde..15a9f5f 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/AbstractCompileTest.java
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/AbstractCompileTest.java
@@ -16,7 +16,7 @@
 
 package org.gradle.api.tasks.compile;
 
-import org.gradle.api.internal.file.SimpleFileCollection;
+import org.gradle.api.internal.file.collections.SimpleFileCollection;
 import org.gradle.api.tasks.AbstractConventionTaskTest;
 import org.gradle.util.WrapUtil;
 import org.junit.Test;
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/javadoc/GroovydocTest.java b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/javadoc/GroovydocTest.java
index c1b1e1e..8ce02f1 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/javadoc/GroovydocTest.java
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/javadoc/GroovydocTest.java
@@ -15,16 +15,18 @@
  */
 package org.gradle.api.tasks.javadoc;
 
+import java.util.Set;
+
 import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.internal.ConventionTask;
+import org.gradle.api.internal.file.collections.SimpleFileCollection;
 import org.gradle.api.tasks.AbstractConventionTaskTest;
 import org.gradle.util.WrapUtil;
-import static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.assertThat;
 import org.junit.Before;
 import org.junit.Test;
 
-import java.util.Set;
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertThat;
 
 /**
  * @author Hans Dockter
@@ -86,4 +88,10 @@ public class GroovydocTest extends AbstractConventionTaskTest {
 
         assertThat(groovydoc.getLinks(), equalTo(newLinkSet));
     }
+
+    @Test(expected = InvalidUserDataException.class)
+    public void groovyClasspathMustNotBeEmpty() {
+        groovydoc.setGroovyClasspath(new SimpleFileCollection());
+        groovydoc.generate();
+    }
 }
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/javadoc/JavadocTest.java b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/javadoc/JavadocTest.java
index fea1308..746ab93 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/javadoc/JavadocTest.java
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/javadoc/JavadocTest.java
@@ -19,7 +19,7 @@ package org.gradle.api.tasks.javadoc;
 import org.gradle.api.GradleException;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.internal.ConventionTask;
-import org.gradle.api.internal.file.SimpleFileCollection;
+import org.gradle.api.internal.file.collections.SimpleFileCollection;
 import org.gradle.api.tasks.AbstractConventionTaskTest;
 import org.gradle.external.javadoc.internal.JavadocExecHandleBuilder;
 import org.gradle.external.javadoc.StandardJavadocDocletOptions;
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/testing/TestTest.java b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/testing/TestTest.java
index 0df765d..919ec4c 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/testing/TestTest.java
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/testing/TestTest.java
@@ -17,11 +17,14 @@
 package org.gradle.api.tasks.testing;
 
 import org.gradle.api.GradleException;
-import org.gradle.api.file.ConfigurableFileTree;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.file.FileTree;
 import org.gradle.api.internal.ConventionTask;
-import org.gradle.api.internal.file.SimpleFileCollection;
+import org.gradle.api.internal.file.CompositeFileTree;
+import org.gradle.api.internal.file.collections.DefaultFileCollectionResolveContext;
+import org.gradle.api.internal.file.collections.SimpleFileCollection;
+import org.gradle.api.internal.file.collections.DirectoryFileTree;
+import org.gradle.api.internal.file.collections.FileTreeAdapter;
 import org.gradle.api.internal.tasks.testing.TestFramework;
 import org.gradle.api.internal.tasks.testing.detection.TestExecuter;
 import org.gradle.api.internal.tasks.testing.junit.JUnitTestFramework;
@@ -42,9 +45,12 @@ import org.junit.runner.RunWith;
 
 import java.io.File;
 import java.util.Collections;
+import java.util.List;
+import java.util.Set;
 
-import static org.gradle.util.Matchers.*;
-import static org.gradle.util.WrapUtil.*;
+import static org.gradle.util.Matchers.isEmpty;
+import static org.gradle.util.WrapUtil.toLinkedSet;
+import static org.gradle.util.WrapUtil.toSet;
 import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.*;
 
@@ -136,11 +142,7 @@ public class TestTest extends AbstractConventionTaskTest {
         test.exclude("exclude");
 
         FileTree classFiles = test.getCandidateClassFiles();
-        assertThat(classFiles, instanceOf(ConfigurableFileTree.class));
-        ConfigurableFileTree files = (ConfigurableFileTree) classFiles;
-        assertThat(files.getDir(), equalTo(classesDir));
-        assertThat(files.getIncludes(), equalTo(toSet("include")));
-        assertThat(files.getExcludes(), equalTo(toSet("exclude")));
+        assertIsDirectoryTree(classFiles, toSet("include"), toSet("exclude"));
     }
 
     @org.junit.Test
@@ -148,10 +150,22 @@ public class TestTest extends AbstractConventionTaskTest {
         configureTask();
         test.setScanForTestClasses(false);
 
-        ConfigurableFileTree files = (ConfigurableFileTree) test.getCandidateClassFiles();
-        assertThat(files.getDir(), equalTo(classesDir));
-        assertThat(files.getIncludes(), equalTo(toSet("**/*Tests.class", "**/*Test.class")));
-        assertThat(files.getExcludes(), equalTo(toSet("**/Abstract*.class")));
+        FileTree classFiles = test.getCandidateClassFiles();
+        assertIsDirectoryTree(classFiles, toSet("**/*Tests.class", "**/*Test.class"), toSet("**/Abstract*.class"));
+    }
+
+    private void assertIsDirectoryTree(FileTree classFiles, Set<String> includes, Set<String> excludes) {
+        assertThat(classFiles, instanceOf(CompositeFileTree.class));
+        CompositeFileTree files = (CompositeFileTree) classFiles;
+        DefaultFileCollectionResolveContext context = new DefaultFileCollectionResolveContext();
+        files.resolve(context);
+        List<? extends FileTree> contents = context.resolveAsFileTrees();
+        FileTreeAdapter adapter = (FileTreeAdapter) contents.get(0);
+        assertThat(adapter.getTree(), instanceOf(DirectoryFileTree.class));
+        DirectoryFileTree directoryFileTree = (DirectoryFileTree) adapter.getTree();
+        assertThat(directoryFileTree.getDir(), equalTo(classesDir));
+        assertThat(directoryFileTree.getPatterns().getIncludes(), equalTo(includes));
+        assertThat(directoryFileTree.getPatterns().getExcludes(), equalTo(excludes));
     }
 
     @org.junit.Test
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/DefaultScalaSourceSet.java b/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/DefaultScalaSourceSet.java
index 5e0b7cf..ba91efe 100644
--- a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/DefaultScalaSourceSet.java
+++ b/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/DefaultScalaSourceSet.java
@@ -16,26 +16,22 @@
 package org.gradle.api.internal.tasks;
 
 import groovy.lang.Closure;
-import org.gradle.api.file.FileTree;
 import org.gradle.api.file.SourceDirectorySet;
 import org.gradle.api.internal.file.DefaultSourceDirectorySet;
 import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.internal.file.UnionFileTree;
 import org.gradle.api.tasks.ScalaSourceSet;
-import org.gradle.api.tasks.util.PatternFilterable;
-import org.gradle.api.tasks.util.PatternSet;
 import org.gradle.util.ConfigureUtil;
 
 public class DefaultScalaSourceSet implements ScalaSourceSet {
     private final SourceDirectorySet scala;
-    private final UnionFileTree allScala;
-    private final PatternFilterable scalaPatterns = new PatternSet();
+    private final SourceDirectorySet allScala;
 
     public DefaultScalaSourceSet(String displayName, FileResolver fileResolver) {
         scala = new DefaultSourceDirectorySet(String.format("%s Scala source", displayName), fileResolver);
         scala.getFilter().include("**/*.java", "**/*.scala");
-        scalaPatterns.include("**/*.scala");
-        allScala = new UnionFileTree(String.format("%s Scala source", displayName), scala.matching(scalaPatterns));
+        allScala = new DefaultSourceDirectorySet(String.format("%s Scala source", displayName), fileResolver);
+        allScala.getFilter().include("**/*.scala");
+        allScala.source(scala);
     }
 
     public SourceDirectorySet getScala() {
@@ -47,11 +43,7 @@ public class DefaultScalaSourceSet implements ScalaSourceSet {
         return this;
     }
 
-    public PatternFilterable getScalaSourcePatterns() {
-        return scalaPatterns;
-    }
-
-    public FileTree getAllScala() {
+    public SourceDirectorySet getAllScala() {
         return allScala;
     }
 }
\ No newline at end of file
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/plugins/scala/ScalaBasePlugin.groovy b/subprojects/scala/src/main/groovy/org/gradle/api/plugins/scala/ScalaBasePlugin.groovy
index e18d080..7e3b403 100644
--- a/subprojects/scala/src/main/groovy/org/gradle/api/plugins/scala/ScalaBasePlugin.groovy
+++ b/subprojects/scala/src/main/groovy/org/gradle/api/plugins/scala/ScalaBasePlugin.groovy
@@ -45,8 +45,8 @@ public class ScalaBasePlugin implements Plugin<Project> {
         project.convention.getPlugin(JavaPluginConvention.class).sourceSets.all {SourceSet sourceSet ->
             sourceSet.convention.plugins.scala = new DefaultScalaSourceSet(sourceSet.displayName, project.fileResolver)
             sourceSet.scala.srcDir { project.file("src/$sourceSet.name/scala")}
-            sourceSet.allJava.add(sourceSet.scala.matching(sourceSet.java.filter))
-            sourceSet.allSource.add(sourceSet.scala)
+            sourceSet.allJava.source(sourceSet.scala)
+            sourceSet.allSource.source(sourceSet.scala)
             sourceSet.resources.filter.exclude { FileTreeElement element -> sourceSet.scala.contains(element.file) }
 
             String taskName = sourceSet.getCompileTaskName('scala')
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/tasks/ScalaSourceSet.java b/subprojects/scala/src/main/groovy/org/gradle/api/tasks/ScalaSourceSet.java
index ac59518..bbe8072 100644
--- a/subprojects/scala/src/main/groovy/org/gradle/api/tasks/ScalaSourceSet.java
+++ b/subprojects/scala/src/main/groovy/org/gradle/api/tasks/ScalaSourceSet.java
@@ -16,7 +16,6 @@
 package org.gradle.api.tasks;
 
 import groovy.lang.Closure;
-import org.gradle.api.file.FileTree;
 import org.gradle.api.file.SourceDirectorySet;
 
 /**
@@ -47,5 +46,5 @@ public interface ScalaSourceSet {
      *
      * @return the Scala source. Never returns null.
      */
-    FileTree getAllScala();
+    SourceDirectorySet getAllScala();
 }
\ No newline at end of file
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaCompile.java b/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaCompile.java
index 7b69a60..504fd3b 100644
--- a/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaCompile.java
+++ b/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaCompile.java
@@ -18,7 +18,6 @@ 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.Factory;
 import org.gradle.api.internal.project.IsolatedAntBuilder;
 import org.gradle.api.internal.tasks.compile.AntJavaCompiler;
 import org.gradle.api.internal.tasks.compile.JavaCompiler;
@@ -38,7 +37,7 @@ public class ScalaCompile extends AbstractCompile {
 
     public ScalaCompile() {
         ScalaCompiler scalaCompiler = new AntScalaCompiler(getServices().get(IsolatedAntBuilder.class));
-        JavaCompiler javaCompiler = new AntJavaCompiler((Factory) getServices().getFactory(AntBuilder.class));
+        JavaCompiler javaCompiler = new AntJavaCompiler(getServices().getFactory(AntBuilder.class));
         compiler = new IncrementalScalaCompiler(new DefaultScalaJavaJointCompiler(scalaCompiler, javaCompiler), getOutputs());
     }
 
diff --git a/subprojects/scala/src/main/resources/META-INF/gradle-plugins/scala-base.properties b/subprojects/scala/src/main/resources/META-INF/gradle-plugins/scala-base.properties
index ebabb57..8848611 100644
--- a/subprojects/scala/src/main/resources/META-INF/gradle-plugins/scala-base.properties
+++ b/subprojects/scala/src/main/resources/META-INF/gradle-plugins/scala-base.properties
@@ -1,16 +1 @@
-#
-# Copyright 2010 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-implementation-class=org.gradle.api.plugins.scala.ScalaBasePlugin
+implementation-class=org.gradle.api.plugins.scala.ScalaBasePlugin
diff --git a/subprojects/scala/src/main/resources/META-INF/gradle-plugins/scala.properties b/subprojects/scala/src/main/resources/META-INF/gradle-plugins/scala.properties
index 2011420..d6c93a7 100644
--- a/subprojects/scala/src/main/resources/META-INF/gradle-plugins/scala.properties
+++ b/subprojects/scala/src/main/resources/META-INF/gradle-plugins/scala.properties
@@ -1,16 +1 @@
-#
-# Copyright 2010 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-implementation-class=org.gradle.api.plugins.scala.ScalaPlugin
+implementation-class=org.gradle.api.plugins.scala.ScalaPlugin
diff --git a/subprojects/scala/src/test/groovy/org/gradle/api/internal/tasks/DefaultScalaSourceSetTest.groovy b/subprojects/scala/src/test/groovy/org/gradle/api/internal/tasks/DefaultScalaSourceSetTest.groovy
index eb3dac1..669ca6a 100644
--- a/subprojects/scala/src/test/groovy/org/gradle/api/internal/tasks/DefaultScalaSourceSetTest.groovy
+++ b/subprojects/scala/src/test/groovy/org/gradle/api/internal/tasks/DefaultScalaSourceSetTest.groovy
@@ -17,11 +17,10 @@ package org.gradle.api.internal.tasks
 
 import org.gradle.api.internal.file.DefaultSourceDirectorySet
 import org.gradle.api.internal.file.FileResolver
-import org.gradle.api.internal.file.UnionFileTree
 import org.junit.Test
-import static org.gradle.util.Matchers.*
+import static org.gradle.util.Matchers.isEmpty
 import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
+import static org.junit.Assert.assertThat
 
 class DefaultScalaSourceSetTest {
     private final DefaultScalaSourceSet sourceSet = new DefaultScalaSourceSet("<set-display-name>", [resolve: {it as File}] as FileResolver)
@@ -31,19 +30,20 @@ class DefaultScalaSourceSetTest {
         assertThat(sourceSet.scala, instanceOf(DefaultSourceDirectorySet))
         assertThat(sourceSet.scala, isEmpty())
         assertThat(sourceSet.scala.displayName, equalTo('<set-display-name> Scala source'))
+        assertThat(sourceSet.scala.filter.includes, equalTo(['**/*.scala', '**/*.java'] as Set))
+        assertThat(sourceSet.scala.filter.excludes, isEmpty())
 
-        assertThat(sourceSet.scalaSourcePatterns.includes, equalTo(['**/*.scala'] as Set))
-        assertThat(sourceSet.scalaSourcePatterns.excludes, isEmpty())
-
-        assertThat(sourceSet.allScala, instanceOf(UnionFileTree))
+        assertThat(sourceSet.allScala, instanceOf(DefaultSourceDirectorySet))
         assertThat(sourceSet.allScala, isEmpty())
         assertThat(sourceSet.allScala.displayName, equalTo('<set-display-name> Scala source'))
-        assertThat(sourceSet.allScala.sourceTrees, not(isEmpty()))
+        assertThat(sourceSet.allScala.source, hasItem(sourceSet.scala))
+        assertThat(sourceSet.allScala.filter.includes, equalTo(['**/*.scala'] as Set))
+        assertThat(sourceSet.allScala.filter.excludes, isEmpty())
     }
 
     @Test
     public void canConfigureScalaSource() {
         sourceSet.scala { srcDir 'src/scala' }
-        assertThat(sourceSet.scala.srcDirs, equalTo([new File('src/scala')] as Set))
+        assertThat(sourceSet.scala.srcDirs, equalTo([new File('src/scala').canonicalFile] as Set))
     }
 }
\ No newline at end of file
diff --git a/subprojects/scala/src/test/groovy/org/gradle/api/plugins/scala/ScalaPluginTest.groovy b/subprojects/scala/src/test/groovy/org/gradle/api/plugins/scala/ScalaPluginTest.groovy
index b0c3035..50ca921 100644
--- a/subprojects/scala/src/test/groovy/org/gradle/api/plugins/scala/ScalaPluginTest.groovy
+++ b/subprojects/scala/src/test/groovy/org/gradle/api/plugins/scala/ScalaPluginTest.groovy
@@ -26,6 +26,7 @@ import static org.gradle.util.WrapUtil.toLinkedSet
 import static org.hamcrest.Matchers.*
 import static org.junit.Assert.assertThat
 import static org.junit.Assert.assertTrue
+import org.gradle.util.Matchers
 
 public class ScalaPluginTest {
 
@@ -85,8 +86,7 @@ public class ScalaPluginTest {
         assertThat(task, dependsOn(JavaPlugin.CLASSES_TASK_NAME))
         assertThat(task.destinationDir, equalTo(project.file("$project.docsDir/scaladoc")))
         assertThat(task.defaultSource, equalTo(project.sourceSets.main.scala))
-        assertThat(task.classpath.sourceCollections, hasItem(project.sourceSets.main.classes))
-        assertThat(task.classpath.sourceCollections, hasItem(project.sourceSets.main.compileClasspath))
+        assertThat(task.classpath, Matchers.sameCollection(project.files(project.sourceSets.main.classes, project.sourceSets.main.compileClasspath)))
         assertThat(task.title, equalTo(project.apiDocTitle))
     }
 
@@ -95,7 +95,6 @@ public class ScalaPluginTest {
 
         def task = project.createTask('otherScaladoc', type: ScalaDoc)
         assertThat(task, dependsOn(JavaPlugin.CLASSES_TASK_NAME))
-        assertThat(task.classpath.sourceCollections, hasItem(project.sourceSets.main.classes))
-        assertThat(task.classpath.sourceCollections, hasItem(project.sourceSets.main.compileClasspath))
+        assertThat(task.classpath, Matchers.sameCollection(project.files(project.sourceSets.main.classes, project.sourceSets.main.compileClasspath)))
     }
 }
diff --git a/subprojects/sonar/sonar.gradle b/subprojects/sonar/sonar.gradle
new file mode 100644
index 0000000..90eb3f6
--- /dev/null
+++ b/subprojects/sonar/sonar.gradle
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+configurations {
+    provided
+    provided.extendsFrom(compile)
+}
+
+dependencies {
+    groovy libraries.groovy
+
+    compile project(':core')
+    compile project(':plugins')
+    compile libraries.slf4j_api
+
+    compile "org.codehaus.sonar:sonar-batch-bootstrapper:2.6"
+    provided "org.codehaus.sonar:sonar-batch:2.6"
+
+    testCompile project(path: ':core', configuration: 'testFixtures')
+
+    testRuntime project(":coreImpl")
+}
+
+sourceSets {
+    main {
+        compileClasspath = configurations.provided
+    }
+}
+
+ideaModule {
+    // TODO: remove everything but then-part after Gradle build has been updated to 1.0-milestone-2
+    if (scopes.PROVIDED) {
+        scopes.PROVIDED.plus += configurations.provided
+    } else {
+        scopes.COMPILE.plus += configurations.provided
+    }
+}
diff --git a/subprojects/sonar/src/main/groovy/org/gradle/api/plugins/sonar/Sonar.groovy b/subprojects/sonar/src/main/groovy/org/gradle/api/plugins/sonar/Sonar.groovy
new file mode 100644
index 0000000..2194140
--- /dev/null
+++ b/subprojects/sonar/src/main/groovy/org/gradle/api/plugins/sonar/Sonar.groovy
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.sonar
+
+import org.sonar.batch.bootstrapper.Bootstrapper
+import org.gradle.api.internal.ConventionTask
+import org.gradle.api.tasks.TaskAction
+import org.gradle.util.ClasspathUtil
+import org.gradle.api.plugins.sonar.internal.ClassesOnlyClassLoader
+import org.gradle.util.GradleVersion
+import org.slf4j.LoggerFactory
+import ch.qos.logback.classic.Logger
+import ch.qos.logback.classic.Level
+
+/**
+ * Analyzes a project and stores the results in Sonar's database.
+ */
+class Sonar extends ConventionTask {
+    /**
+     * The Sonar server to connect to.
+     */
+    String serverUrl
+
+    /**
+     * The directory to be used for caching files downloaded from the Sonar server.
+     */
+    File bootstrapDir
+
+    /**
+     * The base directory for the project to be analyzed.
+     */
+    File projectDir
+
+    /**
+     * The build output directory for the project to be analyzed.
+     */
+    File buildDir
+
+    /**
+     * The directories containing the production sources of the project to be analyzed.
+     */
+    Set<File> projectMainSourceDirs = []
+
+    /**
+     * The directories containing the test sources of the project to be analyzed.
+     */
+    Set<File> projectTestSourceDirs = []
+
+    /**
+     * The directories containing the class files of the project to be analyzed.
+     */
+    Set<File> projectClassesDirs = []
+
+    /**
+     * The dependencies of the project to be analyzed. Typically these will be Jar files.
+     */
+    Set<File> projectDependencies = []
+
+    /**
+     * A unique key for identifying the project to be analyzed.
+     */
+    String projectKey
+
+    /**
+     * The name of the project to be analyzed.
+     */
+    String projectName
+
+    /**
+     * The description of the project to be analyzed.
+     */
+    String projectDescription
+
+    /**
+     * The version of the project to be analyzed.
+     */
+    String projectVersion
+
+    /**
+     * Global properties for use by the Sonar code analyzer.
+     */
+    Map globalProperties = [:]
+
+    /**
+     * Project-specific properties for use by the Sonar code analyzer.
+     */
+    Map projectProperties = [:]
+
+    @TaskAction
+    void execute() {
+        withErrorSqlLogging {
+            getBootstrapDir().mkdirs()
+            def bootstrapper = new Bootstrapper("Gradle", getServerUrl(), getBootstrapDir())
+
+            def classLoader = bootstrapper.createClassLoader(
+                    [findGradleSonarJar()] as URL[], new ClassesOnlyClassLoader(Sonar.classLoader),
+                    "groovy", "org.codehaus.groovy", "org.slf4j", "org.apache.log4j", "org.apache.commons.logging")
+
+            def analyzerClass = classLoader.loadClass("org.gradle.api.plugins.sonar.internal.SonarCodeAnalyzer")
+            def analyzer = analyzerClass.newInstance()
+            analyzer.gradleVersion = GradleVersion.current().version
+            analyzer.sonarTask = this
+            analyzer.execute()
+        }
+    }
+
+    /**
+     * Adds the specified directory to the set of project main source directories.
+     *
+     * @param sourceDirs the main source directory to be added
+     */
+    void projectMainSourceDir(File sourceDir) {
+        projectMainSourceDirs << sourceDir
+    }
+
+    /**
+     * Adds the specified directories to the set of project main source directories.
+     *
+     * @param sourceDirs the main source directories to be added
+     */
+    void projectMainSourceDirs(File... sourceDirs) {
+        projectMainSourceDirs.addAll(sourceDirs as List)
+    }
+
+    /**
+     * Adds the specified directory to the set of project test source directories.
+     *
+     * @param sourceDirs the testsource directory to be added
+     */
+    void projectTestSourceDir(File sourceDir) {
+        projectTestSourceDirs << sourceDir
+    }
+
+    /**
+     * Adds the specified directories to the set of project test source directories.
+     *
+     * @param sourceDirs the test source directories to be added
+     */
+    void projectTestSourceDirs(File... sourceDirs) {
+        projectTestSourceDirs.addAll(sourceDirs as List)
+    }
+
+    /**
+     * Adds the specified directory to the set of project classes directories.
+     *
+     * @param classesDir the classes directory to be added
+     */
+    void projectClassesDir(File classesDir) {
+        projectClassesDirs << classesDir
+    }
+
+    /**
+     * Adds the specified directories to the set of project classes directories.
+     *
+     * @param classesDirs the classes directories to be added
+     */
+    void projectClassesDirs(File... classesDirs) {
+        projectClassesDirs.addAll(classesDirs as List)
+    }
+
+    /**
+     * Adds the specified dependency to the set of project dependencies. Typically this will be a Jar file.
+     *
+     * @param dependency the depedency to be added
+     */
+    void projectDependency(File dependency) {
+        projectDependencies << dependency
+    }
+
+    /**
+     * Adds the specified dependencies to the set of project dependencies. Typically these will be Jar files.
+     *
+     * @param dependencies the dependencies to be added
+     */
+    void projectDependencies(File... dependencies) {
+        projectDependencies.addAll(dependencies as List)
+    }
+
+    /**
+     * Adds the specified property to the map of global properties.
+     * If a property with the specified name already exists, it will
+     * be overwritten.
+     *
+     * @param name the name of the global property
+     * @param value the value of the global property
+     */
+    void globalProperty(String name, String value) {
+        globalProperties.put(name, value)
+    }
+
+    /**
+     * Adds the specified properties to the map of global properties.
+     * Existing properties with the same name will be overwritten.
+     *
+     * @param properties the global properties to be added
+     */
+    void globalProperties(Map properties) {
+        globalProperties.putAll(properties)
+    }
+
+    /**
+     * Adds the specified property to the map of project properties.
+     * If a property with the specified name already exists, it will
+     * be overwritten.
+     *
+     * @param name the name of the project property
+     * @param value the value of the project property
+     */
+    void projectProperty(String name, String value) {
+        globalProperties.put(name, value)
+    }
+
+    /**
+     * Adds the specified properties to the map of project properties.
+     * Existing properties with the same name will be overwritten.
+     *
+     * @param properties the project properties to be added
+     */
+    void projectProperties(Map properties) {
+        projectProperties.putAll(properties)
+    }
+
+    protected URL findGradleSonarJar() {
+        def url = ClasspathUtil.getClasspath(Sonar.classLoader).find { it.path.contains("gradle-sonar") }
+        assert url != null, "failed to detect file system location of gradle-sonar Jar"
+        url
+    }
+
+    // limit Hibernate SQL logging to errors, no matter what the Gradle log level is
+    // this is a workaround for org.sonar.jpa.session.AbstractDatabaseConnector, line 158:
+    // props.put("hibernate.show_sql", Boolean.valueOf(LOG_SQL.isInfoEnabled()).toString());
+    // without this workaround, each SQL statement gets logged even if Gradle log level
+    // is set to QUIET
+    private void withErrorSqlLogging(Closure block) {
+        Logger sqlLogger = (Logger) LoggerFactory.getLogger("org.hibernate.SQL")
+        def oldLevel = sqlLogger.level
+
+        try {
+            sqlLogger.level = Level.ERROR
+            block()
+        } finally {
+            sqlLogger.level = oldLevel
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/sonar/src/main/groovy/org/gradle/api/plugins/sonar/SonarPlugin.groovy b/subprojects/sonar/src/main/groovy/org/gradle/api/plugins/sonar/SonarPlugin.groovy
new file mode 100644
index 0000000..fe5f21f
--- /dev/null
+++ b/subprojects/sonar/src/main/groovy/org/gradle/api/plugins/sonar/SonarPlugin.groovy
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.sonar
+
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.plugins.JavaPlugin
+import org.gradle.api.tasks.SourceSet
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.cache.CacheRepository
+
+/**
+ * A {@link Plugin} for integrating with <a href="http://www.sonarsource.org">Sonar</a>, a web-based platform
+ * for managing code quality. Adds a task named <tt>sonar</tt> with type {@link Sonar} and configures it to
+ * analyze the Java sources in the main source set.
+ */
+class SonarPlugin implements Plugin<Project> {
+    static final String SONAR_TASK_NAME = "sonar"
+
+    void apply(Project project) {
+        project.plugins.withType(JavaPlugin) {
+            def sonarTask = project.tasks.add(SONAR_TASK_NAME, Sonar)
+            configureConventions(sonarTask, project)
+        }
+    }
+
+    private void configureConventions(Sonar sonarTask, Project project) {
+        def main = project.sourceSets.main
+        def test = project.sourceSets.test
+
+        sonarTask.conventionMapping.serverUrl = { "http://localhost:9000" }
+        sonarTask.conventionMapping.bootstrapDir = {
+            def cacheRepository = (project as ProjectInternal).services.get(CacheRepository)
+            cacheRepository.cache("sonar-bootstrap").forObject(project.gradle).open().baseDir
+        }
+        sonarTask.conventionMapping.projectDir = { project.projectDir }
+        sonarTask.conventionMapping.buildDir = { project.buildDir }
+        sonarTask.conventionMapping.projectMainSourceDirs = { getSourceDirs(main) }
+        sonarTask.conventionMapping.projectTestSourceDirs = { getSourceDirs(test) }
+        sonarTask.conventionMapping.projectClassesDirs = { [main.classesDir] as Set }
+        sonarTask.conventionMapping.projectDependencies = { project.configurations.compile.resolve() }
+        sonarTask.conventionMapping.projectKey = { "$project.group:$project.name" as String }
+        sonarTask.conventionMapping.projectName = { project.name }
+        sonarTask.conventionMapping.projectDescription = { project.description }
+        sonarTask.conventionMapping.projectVersion = { project.version as String }
+        sonarTask.conventionMapping.projectProperties = {
+            ["sonar.java.source": project.sourceCompatibility as String,
+             "sonar.java.target": project.targetCompatibility as String,
+             "sonar.dynamicAnalysis": "reuseReports",
+             "sonar.surefire.reportsPath": project.test.testResultsDir as String]
+        }
+    }
+
+    private Set<File> getSourceDirs(SourceSet sourceSet) {
+        sourceSet.allSource.srcDirs as LinkedHashSet
+    }
+}
diff --git a/subprojects/sonar/src/main/groovy/org/gradle/api/plugins/sonar/internal/ClassesOnlyClassLoader.java b/subprojects/sonar/src/main/groovy/org/gradle/api/plugins/sonar/internal/ClassesOnlyClassLoader.java
new file mode 100644
index 0000000..525495f
--- /dev/null
+++ b/subprojects/sonar/src/main/groovy/org/gradle/api/plugins/sonar/internal/ClassesOnlyClassLoader.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.sonar.internal;
+
+import java.net.URL;
+import java.util.Enumeration;
+
+/**
+ * Class loader that cannot load any resources. Used as parent of Sonar
+ * bootstrap class loader to work around http://jira.codehaus.org/browse/SONAR-2276
+ */
+public class ClassesOnlyClassLoader extends ClassLoader {
+    public ClassesOnlyClassLoader(ClassLoader parent) {
+        super(parent);
+    }
+
+    @Override
+    public URL getResource(String name) {
+        return null;
+    }
+
+    @Override
+    public Enumeration<URL> getResources(String name) {
+        return null;
+    }
+}
diff --git a/subprojects/sonar/src/main/groovy/org/gradle/api/plugins/sonar/internal/SonarCodeAnalyzer.groovy b/subprojects/sonar/src/main/groovy/org/gradle/api/plugins/sonar/internal/SonarCodeAnalyzer.groovy
new file mode 100644
index 0000000..396124f
--- /dev/null
+++ b/subprojects/sonar/src/main/groovy/org/gradle/api/plugins/sonar/internal/SonarCodeAnalyzer.groovy
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.sonar.internal
+
+import org.apache.commons.configuration.MapConfiguration
+import org.sonar.api.CoreProperties
+import org.sonar.batch.Batch
+import org.sonar.batch.bootstrapper.EnvironmentInformation
+import org.sonar.batch.bootstrapper.ProjectDefinition
+import org.sonar.batch.bootstrapper.Reactor
+
+/**
+ * Runs Sonar code analysis using the configuration of the Sonar task.
+ */
+class SonarCodeAnalyzer {
+    String gradleVersion
+    def sonarTask
+
+    void execute() {
+        def globalProperties = [:]
+        globalProperties.putAll(sonarTask.globalProperties)
+        globalProperties["sonar.host.url"] = sonarTask.serverUrl
+
+        def projectProperties = new Properties()
+        projectProperties.putAll(sonarTask.projectProperties)
+        projectProperties[CoreProperties.PROJECT_KEY_PROPERTY] = sonarTask.projectKey
+        projectProperties[CoreProperties.PROJECT_NAME_PROPERTY] = sonarTask.projectName
+        projectProperties[CoreProperties.PROJECT_VERSION_PROPERTY] = sonarTask.projectVersion
+
+        def project = new ProjectDefinition(sonarTask.projectDir, sonarTask.bootstrapDir, projectProperties)
+        sonarTask.projectMainSourceDirs.each { project.addSourceDir(it.path) }
+        sonarTask.projectTestSourceDirs.each { project.addTestDir(it.path) }
+        sonarTask.projectClassesDirs.each { project.addBinaryDir(it.path) }
+        sonarTask.projectDependencies.each { project.addLibrary(it.path) }
+
+        def reactor = new Reactor(project)
+        def environment = new EnvironmentInformation("Gradle", gradleVersion)
+        def batch = new Batch(new MapConfiguration(globalProperties), project, reactor, environment)
+        batch.execute()
+    }
+}
diff --git a/subprojects/sonar/src/main/resources/META-INF/gradle-plugins/sonar.properties b/subprojects/sonar/src/main/resources/META-INF/gradle-plugins/sonar.properties
new file mode 100644
index 0000000..791d15e
--- /dev/null
+++ b/subprojects/sonar/src/main/resources/META-INF/gradle-plugins/sonar.properties
@@ -0,0 +1 @@
+implementation-class=org.gradle.api.plugins.sonar.SonarPlugin
\ No newline at end of file
diff --git a/subprojects/sonar/src/test/groovy/org/gradle/api/plugins/sonar/SonarPluginTest.groovy b/subprojects/sonar/src/test/groovy/org/gradle/api/plugins/sonar/SonarPluginTest.groovy
new file mode 100644
index 0000000..e646f39
--- /dev/null
+++ b/subprojects/sonar/src/test/groovy/org/gradle/api/plugins/sonar/SonarPluginTest.groovy
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.sonar
+
+import org.gradle.util.HelperUtil
+import org.gradle.api.plugins.JavaPlugin
+
+import spock.lang.Specification
+
+class SonarPluginTest extends Specification {
+    def "only adds sonar task if Java plugin is present"() {
+        def project = HelperUtil.createRootProject()
+
+        when:
+        project.plugins.apply(SonarPlugin)
+
+        then:
+        !project.tasks.findByName("sonar")
+
+        when:
+        project.plugins.apply(JavaPlugin)
+
+        then:
+        project.tasks.findByName("sonar")
+    }
+
+    def "provides default configuration for sonar task"() {
+        def project = HelperUtil.createRootProject()
+        project.plugins.apply(JavaPlugin)
+        project.sourceSets.main.java.srcDir("src/main/other")
+        project.sourceSets.test.java.srcDir("src/test/other")
+        project.group = "testGroup"
+        project.description = "testDescription"
+        project.sourceCompatibility = "1.6"
+        project.targetCompatibility = "1.5"
+
+        when:
+        project.plugins.apply(SonarPlugin)
+
+        then:
+        def task = (Sonar) project.tasks.getByName("sonar")
+        task.serverUrl == "http://localhost:9000"
+        task.bootstrapDir.isDirectory()
+        task.projectDir == project.projectDir
+        task.buildDir == project.buildDir
+        task.projectMainSourceDirs == [project.file("src/main/java"),
+                project.file("src/main/resources"), project.file("src/main/other")] as Set
+        task.projectTestSourceDirs == [project.file("src/test/java"),
+                project.file("src/test/resources"), project.file("src/test/other")] as Set
+        task.projectClassesDirs == [project.file("build/classes/main")] as Set
+        task.projectDependencies.isEmpty() // because our project doesn't have any dependencies defined
+        task.projectKey == "testGroup:test"
+        task.projectName == "test"
+        task.projectDescription == "testDescription"
+        task.globalProperties.isEmpty()
+        task.projectProperties.size() == 4
+        task.projectProperties["sonar.java.source"] == "1.6"
+        task.projectProperties["sonar.java.target"] == "1.5"
+        task.projectProperties["sonar.dynamicAnalysis"] == "reuseReports"
+        task.projectProperties["sonar.surefire.reportsPath"] == project.file("build/test-results") as String
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/BuildConnection.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/BuildConnection.java
deleted file mode 100644
index b97e926..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/BuildConnection.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.tooling;
-
-import org.gradle.tooling.model.Build;
-
-/**
- * Represents a long-lived connection to a Gradle build.
- *
- * <h2>Thread safety</h2>
- *
- * <p>All implementations of {@code GradleConnection} are thread-safe, and may be shared by any number of threads.</p>
- *
- * <p>All notifications from a given {@code GradleConnection} instance are delivered by a single thread at a time. Note, however, that the delivery thread may change over time.</p>
- */
-public interface BuildConnection {
-    /**
-     * Fetches a snapshot of the model for this build. This method blocks until the model is available.
-     *
-     * @param viewType The model type.
-     * @param <T> The model type.
-     * @return The model.
-     * @throws UnsupportedVersionException When the target Gradle version does not support the given model.
-     * @throws GradleConnectionException On some failure to communicate with Gradle.
-     */
-    <T extends Build> T getModel(Class<T> viewType) throws GradleConnectionException;
-
-    /**
-     * Fetches a snapshot of the model for this build asynchronously. This method return immediately, and the result of the operation is passed to the supplied result handler.
-     *
-     * @param viewType The model type.
-     * @param handler The handler to pass the result to.
-     * @param <T> The model type.
-     */
-    <T extends Build> void getModel(Class<T> viewType, ResultHandler<? super T> handler);
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/BuildException.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/BuildException.java
new file mode 100644
index 0000000..2b912fa
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/BuildException.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling;
+
+/**
+ * Thrown when a Gradle build fails, or when a model cannot be built.
+ */
+public class BuildException extends GradleConnectionException {
+    public BuildException(String message, Throwable throwable) {
+        super(message, throwable);
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/BuildLauncher.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/BuildLauncher.java
new file mode 100644
index 0000000..8d73f75
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/BuildLauncher.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling;
+
+import org.gradle.tooling.model.Task;
+
+import java.io.OutputStream;
+
+/**
+ * <p>A {@code BuildLauncher} allows you to configure and execute a Gradle build.
+ *
+ * <p>You use a {@code BuildLauncher} as follows:
+ *
+ * <ul>
+ *
+ * <li>Create an instance of {@code BuildLauncher} by calling {@link org.gradle.tooling.ProjectConnection#newBuild()}.
+ *
+ * <li>Configure the launcher as appropriate.
+ *
+ * <li>Call either {@link #run()} or {@link #run(ResultHandler)} to execute the build.
+ *
+ * <li>Optionally, you can reuse the launcher to launcher additional builds.
+ *
+ * </ul>
+ *
+ * <p>Instances of {@code BuildLauncher} are not thread-safe.
+ */
+public interface BuildLauncher {
+    /**
+     * Sets the tasks to be executed.
+     *
+     * @param tasks The paths of the tasks to be executed. Relative paths are evaluated relative to the project for which this launcher was created.
+     * @return this
+     */
+    BuildLauncher forTasks(String... tasks);
+
+    /**
+     * Sets the tasks to be executed. Note that the supplied tasks do not necessarily belong to the project which this launcher was created for.
+     *
+     * @param tasks The tasks to be executed.
+     * @return this
+     */
+    BuildLauncher forTasks(Task... tasks);
+
+    /**
+     * Sets the tasks to be executed. Note that the supplied tasks do not necessarily belong to the project which this launcher was created for.
+     *
+     * @param tasks The tasks to be executed.
+     * @return this
+     */
+    BuildLauncher forTasks(Iterable<? extends Task> tasks);
+
+    /**
+     * Sets the {@link OutputStream} that should receive standard output logging from this build. The default is to discard the output.
+     *
+     * @param outputStream The output stream.
+     * @return this
+     */
+    BuildLauncher setStandardOutput(OutputStream outputStream);
+
+    /**
+     * Sets the {@link OutputStream} that should receive standard error logging from this build. The default is to discard the output.
+     *
+     * @param outputStream The output stream.
+     * @return this
+     */
+    BuildLauncher setStandardError(OutputStream outputStream);
+
+    /**
+     * Adds a progress listener which will receive progress events as the build executes.
+     *
+     * @param listener The listener
+     * @return this
+     */
+    BuildLauncher addProgressListener(ProgressListener listener);
+
+    /**
+     * Execute the build, blocking until it is complete.
+     *
+     * @throws UnsupportedVersionException When the target Gradle version does not support the features required for this build.
+     * @throws BuildException On some failure executing the Gradle build.
+     * @throws GradleConnectionException On some other failure using the connection.
+     * @throws IllegalStateException When the connection has been closed or is closing.
+     */
+    void run() throws GradleConnectionException;
+
+    /**
+     * Launchers the build. This method returns immediately, and the result is later passed to the given handler.
+     *
+     * @param handler The handler to supply the result to.
+     * @throws IllegalStateException When the connection has been closed or is closing.
+     */
+    void run(ResultHandler<? super Void> handler) throws IllegalStateException;
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/GradleConnectionException.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/GradleConnectionException.java
index 21250d5..a9ffbe5 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/GradleConnectionException.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/GradleConnectionException.java
@@ -16,7 +16,7 @@
 package org.gradle.tooling;
 
 /**
- * Thrown when there is some problem communicating with Gradle.
+ * Thrown when there is some problem using a Gradle connection.
  */
 public class GradleConnectionException extends RuntimeException {
     public GradleConnectionException(String message) {
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/GradleConnector.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/GradleConnector.java
index 4aa6c71..898b827 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/GradleConnector.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/GradleConnector.java
@@ -15,8 +15,10 @@
  */
 package org.gradle.tooling;
 
+import org.gradle.api.internal.project.ServiceRegistry;
 import org.gradle.tooling.internal.consumer.ConnectionFactory;
-import org.gradle.tooling.internal.consumer.Distribution;
+import org.gradle.tooling.internal.consumer.ConnectorServiceRegistry;
+import org.gradle.tooling.internal.consumer.DefaultGradleConnector;
 import org.gradle.tooling.internal.consumer.DistributionFactory;
 
 import java.io.File;
@@ -29,28 +31,20 @@ import java.net.URI;
  *
  * <li>Call {@link #newConnector()} to create a new connector instance.</li>
  *
- * <li>Configure the connector.</li>
+ * <li>Configure the connector. You must call {@link #forProjectDirectory(java.io.File)} to specify which project you wish to connect to. Other methods are optional.</li>
  *
- * <li>Call {@link #connect()} to create the connection to a build.</li>
+ * <li>Call {@link #connect()} to create the connection to a project.</li>
  *
  * <li>Optionally reuse the connector to create additional connections.</li>
  *
- * <li>When finished with the connections, call {@link #close()} to clean up.</li>
+ * <li>When finished with the connection, call {@link ProjectConnection#close()} to clean up.</li>
  *
  * </ol>
  *
  * <p>{@code GradleConnector} instances are not thread-safe.</p>
  */
-public class GradleConnector {
-    private final ConnectionFactory connectionFactory;
-    private final DistributionFactory distributionFactory;
-    private File projectDir;
-    private Distribution distribution;
-
-    GradleConnector(ConnectionFactory connectionFactory, DistributionFactory distributionFactory) {
-        this.connectionFactory = connectionFactory;
-        this.distributionFactory = distributionFactory;
-    }
+public abstract class GradleConnector {
+    private static final ServiceRegistry SERVICES = new ConnectorServiceRegistry();
 
     /**
      * Creates a new connector instance.
@@ -58,43 +52,35 @@ public class GradleConnector {
      * @return The instance. Never returns null.
      */
     public static GradleConnector newConnector() {
-        return new GradleConnector(new ConnectionFactory(), new DistributionFactory());
+        return new DefaultGradleConnector(SERVICES.get(ConnectionFactory.class), SERVICES.get(DistributionFactory.class));
     }
 
     /**
-     * Specifies which Gradle installation to use. This replaces any value specified using {@link #useDistribution(java.net.URI)} or {@link #useGradleVersion(String)}.
+     * Specifies which Gradle installation to use. This replaces any value specified using {@link #useDistribution(java.net.URI)} or {@link #useGradleVersion(String)}. Defaults to a project-specific
+     * Gradle version.
      *
      * @param gradleHome The Gradle installation directory.
      * @return this
      */
-    public GradleConnector useInstallation(File gradleHome) {
-        distribution = distributionFactory.getDistribution(gradleHome);
-        return this;
-    }
+    public abstract GradleConnector useInstallation(File gradleHome);
 
     /**
      * Specifies which Gradle version to use. The appropriate distribution is downloaded and installed into the user's Gradle home directory. This replaces any value specified using {@link
-     * #useInstallation(java.io.File)} or {@link #useDistribution(java.net.URI)}.
+     * #useInstallation(java.io.File)} or {@link #useDistribution(java.net.URI)}. Defaults to a project-specific Gradle version.
      *
      * @param gradleVersion The version to use.
      * @return this
      */
-    public GradleConnector useGradleVersion(String gradleVersion) {
-        distribution = distributionFactory.getDistribution(gradleVersion);
-        return this;
-    }
+    public abstract GradleConnector useGradleVersion(String gradleVersion);
 
     /**
      * Specifies which Gradle distribution to use. The appropriate distribution is downloaded and installed into the user's Gradle home directory. This replaces any value specified using {@link
-     * #useInstallation(java.io.File)} or {@link #useGradleVersion(String)}.
+     * #useInstallation(java.io.File)} or {@link #useGradleVersion(String)}. Defaults to a project-specific Gradle version.
      *
      * @param gradleDistribution The distribution to use.
      * @return this
      */
-    public GradleConnector useDistribution(URI gradleDistribution) {
-        distribution = distributionFactory.getDistribution(gradleDistribution);
-        return this;
-    }
+    public abstract GradleConnector useDistribution(URI gradleDistribution);
 
     /**
      * Specifies the working directory to use.
@@ -102,32 +88,23 @@ public class GradleConnector {
      * @param projectDir The working directory.
      * @return this
      */
-    public GradleConnector forProjectDirectory(File projectDir) {
-        this.projectDir = projectDir;
-        return this;
-    }
+    public abstract GradleConnector forProjectDirectory(File projectDir);
 
     /**
-     * Creates a connection to the build in the specified project directory.
+     * Specifies the user's Gradle home directory to use. Defaults to {@code ~/.gradle}
+     *
+     * @param gradleUserHomeDir The user's Gradle home directory to use.
+     * @return this
+     */
+    public abstract GradleConnector useGradleUserHomeDir(File gradleUserHomeDir);
+
+    /**
+     * Creates a connection to the project in the specified project directory. You should call {@link org.gradle.tooling.ProjectConnection#close()} when you are finished with the connection.
      *
      * @return The connection. Never return null.
      * @throws UnsupportedVersionException When the target Gradle version does not support this version of the tooling API.
      * @throws GradleConnectionException On failure to establish a connection with the target Gradle version.
      */
-    public BuildConnection connect() throws GradleConnectionException {
-        if (projectDir == null) {
-            throw new IllegalStateException("A project directory must be specified before creating a connection.");
-        }
-        if (distribution == null) {
-            distribution = distributionFactory.getCurrentDistribution();
-        }
-        return connectionFactory.create(distribution, projectDir);
-    }
+    public abstract ProjectConnection connect() throws GradleConnectionException;
 
-    /**
-     * Closes this connector and all connections created by it.
-     */
-    public void close() {
-        connectionFactory.stop();
-    }
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/ModelBuilder.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/ModelBuilder.java
new file mode 100644
index 0000000..7eb1bdb
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/ModelBuilder.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling;
+
+import org.gradle.tooling.model.Project;
+
+import java.io.OutputStream;
+
+/**
+ * <p>A {@code ModelBuilder} allows you to fetch a snapshot of the model for a project.
+ *
+ * <p>You use a {@code ModelBuilder} as follows:
+ *
+ * <ul>
+ *
+ * <li>Create an instance of {@code ModelBuilder} by calling {@link org.gradle.tooling.ProjectConnection#model(Class)}.
+ *
+ * <li>Configure the builder as appropriate.
+ *
+ * <li>Call either {@link #get()} or {@link #get(ResultHandler)} to build the model.
+ *
+ * <li>Optionally, you can reuse the builder to build the model multiple times.
+ *
+ * </ul>
+ *
+ * <p>Instances of {@code ModelBuilder} are not thread-safe.
+ *
+ * @param <T> The type of model to build
+ */
+public interface ModelBuilder<T extends Project> {
+    /**
+     * Sets the {@link OutputStream} which should receive standard output logging generated while building the model. The default is to discard the output.
+     *
+     * @param outputStream The output stream.
+     * @return this
+     */
+    ModelBuilder<T> setStandardOutput(OutputStream outputStream);
+
+    /**
+     * Sets the {@link OutputStream} which should receive standard error logging generated while building the model. The default is to discard the output.
+     *
+     * @param outputStream The output stream.
+     * @return this
+     */
+    ModelBuilder<T> setStandardError(OutputStream outputStream);
+
+    /**
+     * Adds a progress listener which will receive progress events as the model is being built.
+     *
+     * @param listener The listener
+     * @return this
+     */
+    ModelBuilder<T> addProgressListener(ProgressListener listener);
+
+    /**
+     * Fetch the model, blocking until it is available.
+     *
+     * @return The model.
+     * @throws UnsupportedVersionException When the target Gradle version does not support the features required to build this model.
+     * @throws BuildException On some failure executing the Gradle build.
+     * @throws GradleConnectionException On some other failure using the connection.
+     * @throws IllegalStateException When the connection has been closed or is closing.
+     */
+    T get() throws GradleConnectionException;
+
+    /**
+     * Starts fetching the build. This method returns immediately, and the result is later passed to the given handler.
+     *
+     * @param handler The handler to supply the result to.
+     * @throws IllegalStateException When the connection has been closed or is closing.
+     */
+    void get(ResultHandler<? super T> handler) throws IllegalStateException;
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/ProgressEvent.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/ProgressEvent.java
new file mode 100644
index 0000000..6543f46
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/ProgressEvent.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling;
+
+/**
+ * Some information about a piece of work of a long running operation.
+ */
+public interface ProgressEvent {
+    /**
+     * A description of the current piece of work.
+     *
+     * @return The description.
+     */
+    String getDescription();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/ProgressListener.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/ProgressListener.java
new file mode 100644
index 0000000..b6a8d21
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/ProgressListener.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling;
+
+/**
+ * A listener which is notified as some long running operation makes progress.
+ */
+public interface ProgressListener {
+    /**
+     * Called when the progress status changes.
+     *
+     * @param event An event describing the status change.
+     */
+    void statusChanged(ProgressEvent event);
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/ProjectConnection.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/ProjectConnection.java
new file mode 100644
index 0000000..02cf9e7
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/ProjectConnection.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling;
+
+import org.gradle.tooling.model.Project;
+
+/**
+ * Represents a long-lived connection to a Gradle project.
+ *
+ * <h2>Thread safety</h2>
+ *
+ * <p>All implementations of {@code GradleConnection} are thread-safe, and may be shared by any number of threads.</p>
+ *
+ * <p>All notifications from a given {@code GradleConnection} instance are delivered by a single thread at a time. Note, however, that the delivery thread may change over time.</p>
+ */
+public interface ProjectConnection {
+    /**
+     * Fetches a snapshot of the model of the given type for this project.
+     *
+     * <p>This method blocks until the model is available.
+     *
+     * @param viewType The model type.
+     * @param <T> The model type.
+     * @return The model.
+     * @throws UnsupportedVersionException When the target Gradle version does not support the given model.
+     * @throws BuildException On some failure executing the Gradle build, in order to build the model.
+     * @throws GradleConnectionException On some other failure using the connection.
+     * @throws IllegalStateException When this connection has been closed or is closing.
+     */
+    <T extends Project> T getModel(Class<T> viewType) throws GradleConnectionException;
+
+    /**
+     * Fetches a snapshot of the model for this project asynchronously. This method return immediately, and the result of the operation is passed to the supplied result handler.
+     *
+     * @param viewType The model type.
+     * @param handler The handler to pass the result to.
+     * @param <T> The model type.
+     * @throws IllegalStateException When this connection has been closed or is closing.
+     */
+    <T extends Project> void getModel(Class<T> viewType, ResultHandler<? super T> handler) throws IllegalStateException;
+
+    /**
+     * Creates a launcher which can be used to execute a build.
+     *
+     * @return The launcher.
+     */
+    BuildLauncher newBuild();
+
+    /**
+     * Creates a builder which can be used to build the model of the given type.
+     *
+     * @param modelType The model type
+     * @param <T> The model type.
+     * @return The builder.
+     */
+    <T extends Project> ModelBuilder<T> model(Class<T> modelType);
+
+    /**
+     * Closes this connection. Blocks until any pending operations are complete. Once this method has returned, no more notifications will be delivered by any threads.
+     */
+    void close();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/DefaultEclipseProject.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/DefaultEclipseProject.java
new file mode 100644
index 0000000..5c89da4
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/DefaultEclipseProject.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal;
+
+import org.gradle.tooling.internal.protocol.ExternalDependencyVersion1;
+import org.gradle.tooling.internal.protocol.eclipse.EclipseProjectDependencyVersion2;
+import org.gradle.tooling.internal.protocol.eclipse.EclipseProjectVersion3;
+import org.gradle.tooling.internal.protocol.eclipse.EclipseSourceDirectoryVersion1;
+import org.gradle.tooling.internal.protocol.eclipse.EclipseTaskVersion1;
+import org.gradle.util.GUtil;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
+
+public class DefaultEclipseProject implements EclipseProjectVersion3, Serializable {
+    private final String name;
+    private final String path;
+    private EclipseProjectVersion3 parent;
+    private List<ExternalDependencyVersion1> classpath;
+    private final List<EclipseProjectVersion3> children;
+    private List<EclipseSourceDirectoryVersion1> sourceDirectories;
+    private List<EclipseProjectDependencyVersion2> projectDependencies;
+    private final String description;
+    private final File projectDirectory;
+    private Iterable<? extends EclipseTaskVersion1> tasks;
+
+    public DefaultEclipseProject(String name, String path, String description, File projectDirectory, Iterable<? extends EclipseProjectVersion3> children) {
+        this.name = name;
+        this.path = path;
+        this.description = description;
+        this.projectDirectory = projectDirectory;
+        this.tasks = Collections.emptyList();
+        this.children = GUtil.addLists(children);
+        this.classpath = Collections.emptyList();
+        this.sourceDirectories = Collections.emptyList();
+        this.projectDependencies = Collections.emptyList();
+    }
+
+    @Override
+    public String toString() {
+        return String.format("project '%s'", path);
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public EclipseProjectVersion3 getParent() {
+        return parent;
+    }
+
+    public File getProjectDirectory() {
+        return projectDirectory;
+    }
+
+    public void setParent(EclipseProjectVersion3 parent) {
+        this.parent = parent;
+    }
+
+    public List<EclipseProjectVersion3> getChildren() {
+        return children;
+    }
+
+    public Iterable<? extends EclipseSourceDirectoryVersion1> getSourceDirectories() {
+        return sourceDirectories;
+    }
+
+    public void setSourceDirectories(List<EclipseSourceDirectoryVersion1> sourceDirectories) {
+        this.sourceDirectories = sourceDirectories;
+    }
+
+    public Iterable<? extends EclipseProjectDependencyVersion2> getProjectDependencies() {
+        return projectDependencies;
+    }
+
+    public void setProjectDependencies(List<EclipseProjectDependencyVersion2> projectDependencies) {
+        this.projectDependencies = projectDependencies;
+    }
+
+    public List<ExternalDependencyVersion1> getClasspath() {
+        return classpath;
+    }
+    public void setClasspath(List<ExternalDependencyVersion1> classpath) {
+        this.classpath = classpath;
+    }
+
+    public Iterable<? extends EclipseTaskVersion1> getTasks() {
+        return tasks;
+    }
+
+    public void setTasks(Iterable<? extends EclipseTaskVersion1> tasks) {
+        this.tasks = tasks;
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/DefaultEclipseProjectDependency.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/DefaultEclipseProjectDependency.java
new file mode 100644
index 0000000..54ff2d3
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/DefaultEclipseProjectDependency.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal;
+
+import org.gradle.tooling.internal.protocol.eclipse.EclipseProjectDependencyVersion2;
+import org.gradle.tooling.internal.protocol.eclipse.HierarchicalEclipseProjectVersion1;
+
+import java.io.Serializable;
+
+public class DefaultEclipseProjectDependency implements EclipseProjectDependencyVersion2, Serializable {
+    private final String path;
+    private final HierarchicalEclipseProjectVersion1 target;
+
+    public DefaultEclipseProjectDependency(String path, HierarchicalEclipseProjectVersion1 target) {
+        this.target = target;
+        this.path = path;
+    }
+
+    public HierarchicalEclipseProjectVersion1 getTargetProject() {
+        return target;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("project dependency %s (%s)", path, target);
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/DefaultEclipseSourceDirectory.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/DefaultEclipseSourceDirectory.java
new file mode 100644
index 0000000..daca1c2
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/DefaultEclipseSourceDirectory.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal;
+
+import org.gradle.tooling.internal.protocol.eclipse.EclipseSourceDirectoryVersion1;
+
+import java.io.File;
+import java.io.Serializable;
+
+public class DefaultEclipseSourceDirectory implements EclipseSourceDirectoryVersion1, Serializable {
+    private final String path;
+    private final File directory;
+
+    public DefaultEclipseSourceDirectory(String path, File directory) {
+        this.path = path;
+        this.directory = directory;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("source directory '%s'", path);
+    }
+
+    public File getDirectory() {
+        return directory;
+    }
+
+    public String getPath() {
+        return path;
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/DefaultExternalDependency.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/DefaultExternalDependency.java
new file mode 100644
index 0000000..dad304e
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/DefaultExternalDependency.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal;
+
+import org.gradle.tooling.internal.protocol.ExternalDependencyVersion1;
+
+import java.io.File;
+import java.io.Serializable;
+
+public class DefaultExternalDependency implements ExternalDependencyVersion1, Serializable {
+    private final File file;
+    private final File javadoc;
+    private final File source;
+
+    public DefaultExternalDependency(File file, File javadoc, File source) {
+        this.file = file;
+        this.javadoc = javadoc;
+        this.source = source;
+    }
+
+    public File getFile() {
+        return file;
+    }
+
+    public File getJavadoc() {
+        return javadoc;
+    }
+
+    public File getSource() {
+        return source;
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/DefaultTask.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/DefaultTask.java
new file mode 100644
index 0000000..b17b093
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/DefaultTask.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal;
+
+import org.gradle.tooling.internal.protocol.eclipse.EclipseProjectVersion3;
+import org.gradle.tooling.internal.protocol.eclipse.EclipseTaskVersion1;
+
+import java.io.Serializable;
+
+public class DefaultTask implements EclipseTaskVersion1, Serializable {
+    private final EclipseProjectVersion3 project;
+    private final String path;
+    private final String name;
+    private final String description;
+
+    public DefaultTask(EclipseProjectVersion3 project, String path, String name, String description) {
+        this.project = project;
+        this.path = path;
+        this.name = name;
+        this.description = description;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("task '%s'", path);
+    }
+
+    public EclipseProjectVersion3 getProject() {
+        return project;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/AbstractLongRunningOperation.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/AbstractLongRunningOperation.java
new file mode 100644
index 0000000..275cc5f
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/AbstractLongRunningOperation.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer;
+
+import org.gradle.tooling.ProgressListener;
+import org.gradle.tooling.internal.protocol.BuildOperationParametersVersion1;
+import org.gradle.tooling.internal.protocol.ProgressListenerVersion1;
+
+import java.io.File;
+import java.io.OutputStream;
+import java.util.concurrent.TimeUnit;
+
+public class AbstractLongRunningOperation {
+    private OutputStream stdout;
+    private OutputStream stderr;
+    private final ProgressListenerAdapter progressListener = new ProgressListenerAdapter();
+    private final ConnectionParameters parameters;
+
+    public AbstractLongRunningOperation(ConnectionParameters parameters) {
+        this.parameters = parameters;
+    }
+
+    public AbstractLongRunningOperation setStandardOutput(OutputStream outputStream) {
+        stdout = outputStream;
+        return this;
+    }
+
+    public AbstractLongRunningOperation setStandardError(OutputStream outputStream) {
+        stderr = outputStream;
+        return this;
+    }
+
+    public AbstractLongRunningOperation addProgressListener(ProgressListener listener) {
+        progressListener.add(listener);
+        return this;
+    }
+
+    protected BuildOperationParametersVersion1 operationParameters() {
+        return new OperationParameters();
+    }
+
+    private class OperationParameters implements BuildOperationParametersVersion1 {
+        long startTime = System.currentTimeMillis();
+
+        public long getStartTime() {
+            return startTime;
+        }
+
+        public File getGradleUserHomeDir() {
+            return parameters.getGradleUserHomeDir();
+        }
+
+        public File getProjectDir() {
+            return parameters.getProjectDir();
+        }
+
+        public Boolean isSearchUpwards() {
+            return parameters.isSearchUpwards();
+        }
+
+        public Boolean isEmbedded() {
+            return parameters.isEmbedded();
+        }
+
+        public TimeUnit getDaemonMaxIdleTimeUnits() {
+            return parameters.getDaemonMaxIdleTimeUnits();
+        }
+
+        public Integer getDaemonMaxIdleTimeValue() {
+            return parameters.getDaemonMaxIdleTimeValue();
+        }
+
+        public OutputStream getStandardOutput() {
+            return stdout;
+        }
+
+        public OutputStream getStandardError() {
+            return stderr;
+        }
+
+        public ProgressListenerVersion1 getProgressListener() {
+            return progressListener;
+        }
+    }
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/AsyncConnection.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/AsyncConnection.java
new file mode 100644
index 0000000..2c73999
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/AsyncConnection.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer;
+
+import org.gradle.tooling.internal.protocol.*;
+
+public interface AsyncConnection {
+    void executeBuild(BuildParametersVersion1 buildParameters, BuildOperationParametersVersion1 operationParameters, ResultHandlerVersion1<? super Void> handler) throws IllegalStateException;
+
+    void getModel(Class<? extends ProjectVersion3> type, BuildOperationParametersVersion1 operationParameters, ResultHandlerVersion1<? super ProjectVersion3> handler) throws UnsupportedOperationException, IllegalStateException;
+
+    void stop();
+
+    String getDisplayName();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/BlockingResultHandler.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/BlockingResultHandler.java
new file mode 100644
index 0000000..6ec582f
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/BlockingResultHandler.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer;
+
+import org.gradle.tooling.GradleConnectionException;
+import org.gradle.tooling.ResultHandler;
+import org.gradle.util.UncheckedException;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+
+class BlockingResultHandler<T> implements ResultHandler<T> {
+    private final BlockingQueue<Object> queue = new ArrayBlockingQueue<Object>(1);
+    private final Class<T> resultType;
+    private static final Object NULL = new Object();
+
+    public BlockingResultHandler(Class<T> resultType) {
+        this.resultType = resultType;
+    }
+
+    public T getResult() {
+        Object result;
+        try {
+            result = queue.take();
+        } catch (InterruptedException e) {
+            throw UncheckedException.asUncheckedException(e);
+        }
+
+        if (result instanceof Throwable) {
+            throw UncheckedException.asUncheckedException((Throwable) result);
+        }
+        if (result == NULL) {
+            return null;
+        }
+        return resultType.cast(result);
+    }
+
+    public void onComplete(T result) {
+        queue.add(result == null ? NULL : result);
+    }
+
+    public void onFailure(GradleConnectionException failure) {
+        queue.add(failure);
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/CachingToolingImplementationLoader.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/CachingToolingImplementationLoader.java
index 76054cc..676c8ed 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/CachingToolingImplementationLoader.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/CachingToolingImplementationLoader.java
@@ -15,27 +15,30 @@
  */
 package org.gradle.tooling.internal.consumer;
 
-import org.gradle.tooling.internal.protocol.ConnectionFactoryVersion1;
+import org.gradle.tooling.internal.protocol.ConnectionVersion4;
 
 import java.io.File;
-import java.util.*;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
 
 public class CachingToolingImplementationLoader implements ToolingImplementationLoader {
     private final ToolingImplementationLoader loader;
-    private final Map<Set<File>, ConnectionFactoryVersion1> connections = new HashMap<Set<File>, ConnectionFactoryVersion1>();
+    private final Map<Set<File>, ConnectionVersion4> connections = new HashMap<Set<File>, ConnectionVersion4>();
 
     public CachingToolingImplementationLoader(ToolingImplementationLoader loader) {
         this.loader = loader;
     }
 
-    public ConnectionFactoryVersion1 create(Distribution distribution) {
+    public ConnectionVersion4 create(Distribution distribution) {
         Set<File> classpath = new LinkedHashSet<File>(distribution.getToolingImplementationClasspath());
-        ConnectionFactoryVersion1 factory = connections.get(classpath);
-        if (factory == null) {
-            factory = loader.create(distribution);
-            connections.put(classpath, factory);
+        ConnectionVersion4 connection = connections.get(classpath);
+        if (connection == null) {
+            connection = loader.create(distribution);
+            connections.put(classpath, connection);
         }
 
-        return factory;
+        return connection;
     }
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ConnectionFactory.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ConnectionFactory.java
index e3d566f..e8f7220 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ConnectionFactory.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ConnectionFactory.java
@@ -15,43 +15,33 @@
  */
 package org.gradle.tooling.internal.consumer;
 
-import org.gradle.messaging.concurrent.Stoppable;
-import org.gradle.tooling.BuildConnection;
-import org.gradle.tooling.internal.protocol.ConnectionFactoryVersion1;
-import org.gradle.tooling.internal.protocol.ConnectionVersion1;
-
-import java.io.File;
-import java.util.Set;
-import java.util.concurrent.CopyOnWriteArraySet;
+import org.gradle.listener.ListenerManager;
+import org.gradle.logging.ProgressLoggerFactory;
+import org.gradle.messaging.concurrent.DefaultExecutorFactory;
+import org.gradle.tooling.ProjectConnection;
+import org.gradle.tooling.internal.protocol.ConnectionVersion4;
 
 /**
  * This is the main internal entry point for the tooling API.
  *
  * This implementation is thread-safe.
  */
-public class ConnectionFactory implements Stoppable {
+public class ConnectionFactory {
     private final ProtocolToModelAdapter adapter = new ProtocolToModelAdapter();
     private final ToolingImplementationLoader toolingImplementationLoader;
-    private final Set<ConnectionFactoryVersion1> factories = new CopyOnWriteArraySet<ConnectionFactoryVersion1>();
-
-    public ConnectionFactory() {
-        this(new CachingToolingImplementationLoader(new DefaultToolingImplementationLoader()));
-    }
+    private final DefaultExecutorFactory executorFactory = new DefaultExecutorFactory();
+    private final ListenerManager listenerManager;
+    private final ProgressLoggerFactory progressLoggerFactory;
 
-    ConnectionFactory(ToolingImplementationLoader toolingImplementationLoader) {
+    public ConnectionFactory(ToolingImplementationLoader toolingImplementationLoader, ListenerManager listenerManager, ProgressLoggerFactory progressLoggerFactory) {
         this.toolingImplementationLoader = toolingImplementationLoader;
+        this.listenerManager = listenerManager;
+        this.progressLoggerFactory = progressLoggerFactory;
     }
 
-    public BuildConnection create(Distribution distribution, File projectDir) {
-        ConnectionFactoryVersion1 factory = toolingImplementationLoader.create(distribution);
-        factories.add(factory);
-        final ConnectionVersion1 connection = factory.create(projectDir);
-        return new DefaultBuildConnection(connection, adapter);
-    }
-
-    public void stop() {
-        for (ConnectionFactoryVersion1 factory : factories) {
-            factory.stop();
-        }
+    public ProjectConnection create(Distribution distribution, ConnectionParameters parameters) {
+        ConnectionVersion4 connection = new ProgressLoggingConnection(new LazyConnection(distribution, toolingImplementationLoader), progressLoggerFactory, listenerManager);
+        AsyncConnection asyncConnection = new DefaultAsyncConnection(connection, executorFactory);
+        return new DefaultProjectConnection(asyncConnection, adapter, parameters);
     }
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ConnectionParameters.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ConnectionParameters.java
new file mode 100644
index 0000000..accf502
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ConnectionParameters.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer;
+
+import java.io.File;
+import java.util.concurrent.TimeUnit;
+
+public interface ConnectionParameters {
+    File getProjectDir();
+
+    /**
+     * Specifies whether to search for root project, or null to use default.
+     */
+    Boolean isSearchUpwards();
+
+    /**
+     * Returns the Gradle user home directory, or null to use default.
+     */
+    File getGradleUserHomeDir();
+
+    Boolean isEmbedded();
+
+    Integer getDaemonMaxIdleTimeValue();
+
+    TimeUnit getDaemonMaxIdleTimeUnits();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ConnectorServiceRegistry.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ConnectorServiceRegistry.java
new file mode 100644
index 0000000..c4ad4b3
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ConnectorServiceRegistry.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer;
+
+import org.gradle.StartParameter;
+import org.gradle.api.internal.project.DefaultServiceRegistry;
+import org.gradle.listener.DefaultListenerManager;
+import org.gradle.listener.ListenerManager;
+import org.gradle.logging.ProgressLoggerFactory;
+import org.gradle.logging.internal.DefaultProgressLoggerFactory;
+import org.gradle.logging.internal.ProgressListener;
+import org.gradle.util.TrueTimeProvider;
+
+public class ConnectorServiceRegistry extends DefaultServiceRegistry {
+    protected ListenerManager createListenerManager() {
+        return new DefaultListenerManager();
+    }
+
+    protected ProgressLoggerFactory createProgressLoggerFactory() {
+        return new DefaultProgressLoggerFactory(get(ListenerManager.class).getBroadcaster(ProgressListener.class), new TrueTimeProvider());
+    }
+
+    protected ToolingImplementationLoader createToolingImplementationLoader() {
+        return new CachingToolingImplementationLoader(new DefaultToolingImplementationLoader());
+    }
+
+    protected ConnectionFactory createConnectionFactory() {
+        return new ConnectionFactory(get(ToolingImplementationLoader.class), get(ListenerManager.class), get(ProgressLoggerFactory.class));
+    }
+
+    protected DistributionFactory createDistributionFactory() {
+        return new DistributionFactory(StartParameter.DEFAULT_GRADLE_USER_HOME, get(ProgressLoggerFactory.class));
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultAsyncConnection.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultAsyncConnection.java
new file mode 100644
index 0000000..c064fcb
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultAsyncConnection.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer;
+
+import org.gradle.messaging.concurrent.ExecutorFactory;
+import org.gradle.messaging.concurrent.StoppableExecutor;
+import org.gradle.tooling.internal.protocol.*;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Adapts a {@link ConnectionVersion4} to an {@link AsyncConnection}.
+ */
+class DefaultAsyncConnection implements AsyncConnection {
+    private final ConnectionVersion4 connection;
+    private final StoppableExecutor executor;
+    private final AtomicBoolean closed = new AtomicBoolean();
+
+    public DefaultAsyncConnection(ConnectionVersion4 connection, ExecutorFactory executorFactory) {
+        this.connection = connection;
+        executor = executorFactory.create("Connection worker");
+    }
+
+    public String getDisplayName() {
+        return connection.getMetaData().getDisplayName();
+    }
+
+    public void executeBuild(final BuildParametersVersion1 buildParameters, final BuildOperationParametersVersion1 operationParameters, ResultHandlerVersion1<? super Void> handler) throws IllegalStateException {
+        runLater(handler, new ConnectionAction<Void>() {
+            public Void run() {
+                connection.executeBuild(buildParameters, operationParameters);
+                return null;
+            }
+        });
+    }
+
+    public void getModel(final Class<? extends ProjectVersion3> type, final BuildOperationParametersVersion1 operationParameters, ResultHandlerVersion1<? super ProjectVersion3> handler) throws UnsupportedOperationException, IllegalStateException {
+        runLater(handler, new ConnectionAction<ProjectVersion3>() {
+            public ProjectVersion3 run() {
+                return connection.getModel(type, operationParameters);
+            }
+        });
+    }
+
+    public void stop() {
+        closed.set(true);
+        executor.stop();
+        connection.stop();
+    }
+
+    private <T> void runLater(final ResultHandlerVersion1<? super T> handler, final ConnectionAction<T> action) {
+        onStartOperation();
+
+        executor.execute(new Runnable() {
+            public void run() {
+                T result;
+                try {
+                    result = action.run();
+                } catch (Throwable t) {
+                    handler.onFailure(t);
+                    return;
+                }
+                handler.onComplete(result);
+            }
+        });
+    }
+
+    private void onStartOperation() {
+        if (closed.get()) {
+            throw new IllegalStateException("This connection has been closed.");
+        }
+    }
+
+    private interface ConnectionAction<T> {
+        T run();
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultBuildConnection.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultBuildConnection.java
deleted file mode 100644
index 821da5b..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultBuildConnection.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.tooling.internal.consumer;
-
-import org.gradle.tooling.BuildConnection;
-import org.gradle.tooling.GradleConnectionException;
-import org.gradle.tooling.ResultHandler;
-import org.gradle.tooling.UnsupportedVersionException;
-import org.gradle.tooling.internal.protocol.BuildVersion1;
-import org.gradle.tooling.internal.protocol.ConnectionVersion1;
-import org.gradle.tooling.internal.protocol.ResultHandlerVersion1;
-import org.gradle.tooling.internal.protocol.eclipse.EclipseBuildVersion1;
-import org.gradle.tooling.model.Build;
-import org.gradle.tooling.model.eclipse.EclipseBuild;
-import org.gradle.util.UncheckedException;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.BlockingQueue;
-
-class DefaultBuildConnection implements BuildConnection {
-    private final ConnectionVersion1 connection;
-    private final Map<Class<? extends Build>, Class<? extends BuildVersion1>> modelTypeMap = new HashMap<Class<? extends Build>, Class<? extends BuildVersion1>>();
-    private ProtocolToModelAdapter adapter;
-
-    public DefaultBuildConnection(ConnectionVersion1 connection, ProtocolToModelAdapter adapter) {
-        this.connection = connection;
-        this.adapter = adapter;
-        modelTypeMap.put(Build.class, BuildVersion1.class);
-        modelTypeMap.put(EclipseBuild.class, EclipseBuildVersion1.class);
-    }
-
-    public <T extends Build> T getModel(Class<T> viewType) {
-        final BlockingQueue<Object> queue = new ArrayBlockingQueue<Object>(20);
-        getModel(viewType, new ResultHandler<T>() {
-            public void onComplete(T result) {
-                queue.add(result);
-            }
-
-            public void onFailure(GradleConnectionException failure) {
-                queue.add(failure);
-            }
-        });
-
-        Object result;
-        try {
-            result = queue.take();
-        } catch (InterruptedException e) {
-            throw UncheckedException.asUncheckedException(e);
-        }
-
-        if (result instanceof GradleConnectionException) {
-            throw (GradleConnectionException) result;
-        }
-        return viewType.cast(result);
-    }
-
-    public <T extends Build> void getModel(final Class<T> viewType, final ResultHandler<? super T> handler) {
-        connection.getModel(mapToProtocol(viewType), new ResultHandlerVersion1<BuildVersion1>() {
-            public void onComplete(BuildVersion1 result) {
-                handler.onComplete(adapter.adapt(viewType, result));
-            }
-
-            public void onFailure(Throwable failure) {
-                handler.onFailure(new GradleConnectionException(String.format("Could not fetch model of type '%s' from %s.", viewType.getSimpleName(), connection.getDisplayName()), failure));
-            }
-        });
-    }
-
-    private Class<? extends BuildVersion1> mapToProtocol(Class<? extends Build> viewType) {
-        Class<? extends BuildVersion1> protocolViewType = modelTypeMap.get(viewType);
-        if (protocolViewType == null) {
-            throw new UnsupportedVersionException(String.format("Model of type '%s' is not supported.", viewType.getSimpleName()));
-        }
-        return protocolViewType;
-    }
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultBuildLauncher.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultBuildLauncher.java
new file mode 100644
index 0000000..be49b9c
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultBuildLauncher.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer;
+
+import org.gradle.tooling.BuildLauncher;
+import org.gradle.tooling.ProgressListener;
+import org.gradle.tooling.ResultHandler;
+import org.gradle.tooling.internal.protocol.BuildParametersVersion1;
+import org.gradle.tooling.model.Task;
+
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+class DefaultBuildLauncher extends AbstractLongRunningOperation implements BuildLauncher {
+    private final List<String> tasks = new ArrayList<String>();
+    private final AsyncConnection connection;
+
+    public DefaultBuildLauncher(AsyncConnection connection, ConnectionParameters parameters) {
+        super(parameters);
+        this.connection = connection;
+    }
+
+    public BuildLauncher forTasks(String... tasks) {
+        this.tasks.clear();
+        this.tasks.addAll(Arrays.asList(tasks));
+        return this;
+    }
+
+    public BuildLauncher forTasks(Task... tasks) {
+        forTasks(Arrays.asList(tasks));
+        return this;
+    }
+
+    public BuildLauncher forTasks(Iterable<? extends Task> tasks) {
+        this.tasks.clear();
+        for (Task task : tasks) {
+            this.tasks.add(task.getPath());
+        }
+        return this;
+    }
+
+    @Override
+    public DefaultBuildLauncher setStandardError(OutputStream outputStream) {
+        super.setStandardError(outputStream);
+        return this;
+    }
+
+    @Override
+    public DefaultBuildLauncher setStandardOutput(OutputStream outputStream) {
+        super.setStandardOutput(outputStream);
+        return this;
+    }
+
+    @Override
+    public DefaultBuildLauncher addProgressListener(ProgressListener listener) {
+        super.addProgressListener(listener);
+        return this;
+    }
+
+    public void run() {
+        BlockingResultHandler<Void> handler = new BlockingResultHandler<Void>(Void.class);
+        run(handler);
+        handler.getResult();
+    }
+
+    public void run(final ResultHandler<? super Void> handler) {
+        connection.executeBuild(new DefaultBuildParameters(), operationParameters(), new ResultHandlerAdapter<Void>(handler){
+            @Override
+            protected String connectionFailureMessage(Throwable failure) {
+                return String.format("Could not execute build using %s.", connection.getDisplayName());
+            }
+        });
+    }
+
+    private class DefaultBuildParameters implements BuildParametersVersion1 {
+        public List<String> getTasks() {
+            return tasks;
+        }
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultConnectionParameters.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultConnectionParameters.java
new file mode 100644
index 0000000..1057844
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultConnectionParameters.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer;
+
+import java.io.File;
+import java.util.concurrent.TimeUnit;
+
+public class DefaultConnectionParameters implements ConnectionParameters {
+    private final File gradleUserHomeDir;
+    private final File projectDir;
+    private final Boolean searchUpwards;
+    private final Boolean embedded;
+    private final Integer daemonMaxIdleTimeValue;
+    private final TimeUnit daemonMaxIdleTimeUnits;
+
+    public DefaultConnectionParameters(File projectDir, File gradleUserHomeDir, Boolean searchUpwards, Boolean embedded, Integer daemonMaxIdleTimeValue, TimeUnit daemonMaxIdleTimeUnits) {
+        this.projectDir = projectDir;
+        this.gradleUserHomeDir = gradleUserHomeDir;
+        this.searchUpwards = searchUpwards;
+        this.embedded = embedded;
+        this.daemonMaxIdleTimeValue = daemonMaxIdleTimeValue;
+        this.daemonMaxIdleTimeUnits = daemonMaxIdleTimeUnits;
+    }
+
+    public File getGradleUserHomeDir() {
+        return gradleUserHomeDir;
+    }
+
+    public File getProjectDir() {
+        return projectDir;
+    }
+
+    public Boolean isSearchUpwards() {
+        return searchUpwards;
+    }
+
+    public Boolean isEmbedded() {
+        return embedded;
+    }
+
+    public Integer getDaemonMaxIdleTimeValue() {
+        return daemonMaxIdleTimeValue;
+    }
+
+    public TimeUnit getDaemonMaxIdleTimeUnits() {
+        return daemonMaxIdleTimeUnits;
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultGradleConnector.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultGradleConnector.java
new file mode 100644
index 0000000..4cc8222
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultGradleConnector.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer;
+
+import org.gradle.tooling.ProjectConnection;
+import org.gradle.tooling.GradleConnectionException;
+import org.gradle.tooling.GradleConnector;
+import org.gradle.util.GradleVersion;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.net.URI;
+import java.util.concurrent.TimeUnit;
+
+public class DefaultGradleConnector extends GradleConnector {
+    private static final Logger LOGGER = LoggerFactory.getLogger(GradleConnector.class);
+    private final ConnectionFactory connectionFactory;
+    private final DistributionFactory distributionFactory;
+    private File projectDir;
+    private File gradleUserHomeDir;
+    private Distribution distribution;
+    private Boolean searchUpwards;
+    private Boolean embedded;
+    private Integer daemonMaxIdleTimeValue;
+    private TimeUnit daemonMaxIdleTimeUnits;
+
+    public DefaultGradleConnector(ConnectionFactory connectionFactory, DistributionFactory distributionFactory) {
+        this.connectionFactory = connectionFactory;
+        this.distributionFactory = distributionFactory;
+    }
+
+    public GradleConnector useInstallation(File gradleHome) {
+        distribution = distributionFactory.getDistribution(gradleHome);
+        return this;
+    }
+
+    public GradleConnector useGradleVersion(String gradleVersion) {
+        distribution = distributionFactory.getDistribution(gradleVersion);
+        return this;
+    }
+
+    public GradleConnector useDistribution(URI gradleDistribution) {
+        distribution = distributionFactory.getDistribution(gradleDistribution);
+        return this;
+    }
+
+    public GradleConnector useClasspathDistribution() {
+        distribution = distributionFactory.getClasspathDistribution();
+        return this;
+    }
+
+    public GradleConnector forProjectDirectory(File projectDir) {
+        this.projectDir = projectDir;
+        return this;
+    }
+
+    public GradleConnector useGradleUserHomeDir(File gradleUserHomeDir) {
+        this.gradleUserHomeDir = gradleUserHomeDir;
+        return this;
+    }
+
+    public GradleConnector searchUpwards(boolean searchUpwards) {
+        this.searchUpwards = searchUpwards;
+        return this;
+    }
+
+    public GradleConnector embedded(boolean embedded) {
+        this.embedded = embedded;
+        return this;
+    }
+
+    public GradleConnector daemonMaxIdleTime(int timeoutValue, TimeUnit timeoutUnits) {
+        this.daemonMaxIdleTimeValue = timeoutValue;
+        this.daemonMaxIdleTimeUnits = timeoutUnits;
+        return this;
+    }
+
+    public ProjectConnection connect() throws GradleConnectionException {
+        LOGGER.debug("Connecting from tooling API consumer version {}", GradleVersion.current().getVersion());
+
+        if (projectDir == null) {
+            throw new IllegalStateException("A project directory must be specified before creating a connection.");
+        }
+        if (distribution == null) {
+            distribution = distributionFactory.getDefaultDistribution(projectDir);
+        }
+        return connectionFactory.create(distribution, new DefaultConnectionParameters(projectDir, gradleUserHomeDir, searchUpwards, embedded, daemonMaxIdleTimeValue, daemonMaxIdleTimeUnits));
+    }
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultModelBuilder.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultModelBuilder.java
new file mode 100644
index 0000000..6a7b984
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultModelBuilder.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer;
+
+import org.gradle.tooling.GradleConnectionException;
+import org.gradle.tooling.ModelBuilder;
+import org.gradle.tooling.ProgressListener;
+import org.gradle.tooling.ResultHandler;
+import org.gradle.tooling.internal.protocol.ProjectVersion3;
+import org.gradle.tooling.model.Project;
+
+import java.io.OutputStream;
+
+public class DefaultModelBuilder<T extends Project> extends AbstractLongRunningOperation implements ModelBuilder<T> {
+    private final Class<T> modelType;
+    private final Class<? extends ProjectVersion3> protocolType;
+    private final AsyncConnection connection;
+    private final ProtocolToModelAdapter adapter;
+
+    public DefaultModelBuilder(Class<T> modelType, Class<? extends ProjectVersion3> protocolType, AsyncConnection connection, ProtocolToModelAdapter adapter, ConnectionParameters parameters) {
+        super(parameters);
+        this.modelType = modelType;
+        this.protocolType = protocolType;
+        this.connection = connection;
+        this.adapter = adapter;
+    }
+
+    public T get() throws GradleConnectionException {
+        BlockingResultHandler<T> handler = new BlockingResultHandler<T>(modelType);
+        get(handler);
+        return handler.getResult();
+    }
+
+    public void get(final ResultHandler<? super T> handler) throws IllegalStateException {
+        ResultHandler<ProjectVersion3> adaptingHandler = new ProtocolToModelAdaptingHandler(handler);
+        connection.getModel(protocolType, operationParameters(), new ResultHandlerAdapter<ProjectVersion3>(adaptingHandler) {
+            @Override
+            protected String connectionFailureMessage(Throwable failure) {
+                return String.format("Could not fetch model of type '%s' using %s.", modelType.getSimpleName(), connection.getDisplayName());
+            }
+        });
+    }
+
+    @Override
+    public DefaultModelBuilder<T> setStandardOutput(OutputStream outputStream) {
+        super.setStandardOutput(outputStream);
+        return this;
+    }
+
+    @Override
+    public DefaultModelBuilder<T> setStandardError(OutputStream outputStream) {
+        super.setStandardError(outputStream);
+        return this;
+    }
+
+    @Override
+    public DefaultModelBuilder<T> addProgressListener(ProgressListener listener) {
+        super.addProgressListener(listener);
+        return this;
+    }
+
+    private class ProtocolToModelAdaptingHandler implements ResultHandler<ProjectVersion3> {
+        private final ResultHandler<? super T> handler;
+
+        public ProtocolToModelAdaptingHandler(ResultHandler<? super T> handler) {
+            this.handler = handler;
+        }
+
+        public void onComplete(ProjectVersion3 result) {
+            handler.onComplete(adapter.adapt(modelType, result));
+
+        }
+
+        public void onFailure(GradleConnectionException failure) {
+            handler.onFailure(failure);
+        }
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultProjectConnection.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultProjectConnection.java
new file mode 100644
index 0000000..7ec2731
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultProjectConnection.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer;
+
+import org.gradle.tooling.*;
+import org.gradle.tooling.internal.protocol.BuildableProjectVersion1;
+import org.gradle.tooling.internal.protocol.HierarchicalProjectVersion1;
+import org.gradle.tooling.internal.protocol.ProjectVersion3;
+import org.gradle.tooling.internal.protocol.eclipse.EclipseProjectVersion3;
+import org.gradle.tooling.internal.protocol.eclipse.HierarchicalEclipseProjectVersion1;
+import org.gradle.tooling.model.BuildableProject;
+import org.gradle.tooling.model.HierarchicalProject;
+import org.gradle.tooling.model.Project;
+import org.gradle.tooling.model.eclipse.EclipseProject;
+import org.gradle.tooling.model.eclipse.HierarchicalEclipseProject;
+
+import java.util.HashMap;
+import java.util.Map;
+
+class DefaultProjectConnection implements ProjectConnection {
+    private final AsyncConnection connection;
+    private final Map<Class<? extends Project>, Class<? extends ProjectVersion3>> modelTypeMap = new HashMap<Class<? extends Project>, Class<? extends ProjectVersion3>>();
+    private ProtocolToModelAdapter adapter;
+    private final ConnectionParameters parameters;
+
+    public DefaultProjectConnection(AsyncConnection connection, ProtocolToModelAdapter adapter, ConnectionParameters parameters) {
+        this.connection = connection;
+        this.parameters = parameters;
+        this.adapter = adapter;
+        modelTypeMap.put(Project.class, ProjectVersion3.class);
+        modelTypeMap.put(BuildableProject.class, BuildableProjectVersion1.class);
+        modelTypeMap.put(HierarchicalProject.class, HierarchicalProjectVersion1.class);
+        modelTypeMap.put(HierarchicalEclipseProject.class, HierarchicalEclipseProjectVersion1.class);
+        modelTypeMap.put(EclipseProject.class, EclipseProjectVersion3.class);
+    }
+
+    public void close() {
+        connection.stop();
+    }
+
+    public <T extends Project> T getModel(Class<T> viewType) {
+        return model(viewType).get();
+    }
+
+    public <T extends Project> void getModel(final Class<T> viewType, final ResultHandler<? super T> handler) {
+        model(viewType).get(handler);
+    }
+
+    public BuildLauncher newBuild() {
+        return new DefaultBuildLauncher(connection, parameters);
+    }
+
+    public <T extends Project> ModelBuilder<T> model(Class<T> modelType) {
+        return new DefaultModelBuilder<T>(modelType, mapToProtocol(modelType), connection, adapter, parameters);
+    }
+
+    private Class<? extends ProjectVersion3> mapToProtocol(Class<? extends Project> viewType) {
+        Class<? extends ProjectVersion3> protocolViewType = modelTypeMap.get(viewType);
+        if (protocolViewType == null) {
+            throw new UnsupportedVersionException(String.format("Model of type '%s' is not supported.", viewType.getSimpleName()));
+        }
+        return protocolViewType;
+    }
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultToolingImplementationLoader.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultToolingImplementationLoader.java
index 47e54a3..f5670d5 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultToolingImplementationLoader.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultToolingImplementationLoader.java
@@ -16,43 +16,67 @@
 package org.gradle.tooling.internal.consumer;
 
 import org.gradle.tooling.GradleConnectionException;
-import org.gradle.tooling.internal.protocol.ConnectionFactoryVersion1;
+import org.gradle.tooling.UnsupportedVersionException;
+import org.gradle.tooling.internal.protocol.ConnectionVersion4;
 import org.gradle.util.FilteringClassLoader;
 import org.gradle.util.GFileUtils;
+import org.gradle.util.GradleVersion;
 import org.gradle.util.ObservableUrlClassLoader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.BufferedReader;
+import java.io.File;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.net.URL;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 public class DefaultToolingImplementationLoader implements ToolingImplementationLoader {
+    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultToolingImplementationLoader.class);
     private final ClassLoader classLoader;
 
     public DefaultToolingImplementationLoader() {
-        classLoader = getClass().getClassLoader();
+        this(DefaultToolingImplementationLoader.class.getClassLoader());
     }
 
-    public ConnectionFactoryVersion1 create(Distribution distribution) {
+    DefaultToolingImplementationLoader(ClassLoader classLoader) {
+        this.classLoader = classLoader;
+    }
+
+    public ConnectionVersion4 create(Distribution distribution) {
+        LOGGER.debug("Using tooling provider from {}", distribution.getDisplayName());
         ClassLoader classLoader = createImplementationClassLoader(distribution);
-        String implementationClassName = loadImplementationClassName(classLoader);
+        String implementationClassName = loadImplementationClassName(classLoader, distribution);
         try {
-            return (ConnectionFactoryVersion1) classLoader.loadClass(implementationClassName).newInstance();
+            return (ConnectionVersion4) classLoader.loadClass(implementationClassName).newInstance();
         } catch (Throwable t) {
             throw new GradleConnectionException(String.format("Could not create an instance of Tooling API implementation class '%s'.", implementationClassName), t);
         }
     }
 
     private ClassLoader createImplementationClassLoader(Distribution distribution) {
-        URL[] urls = GFileUtils.toURLArray(distribution.getToolingImplementationClasspath());
+        Set<File> implementationClasspath = distribution.getToolingImplementationClasspath();
+        LOGGER.debug("Using tooling provider classpath: {}", implementationClasspath);
+        URL[] urls = GFileUtils.toURLArray(implementationClasspath);
         FilteringClassLoader filteringClassLoader = new FilteringClassLoader(classLoader);
         filteringClassLoader.allowPackage("org.gradle.tooling.internal.protocol");
         return new ObservableUrlClassLoader(filteringClassLoader, urls);
     }
 
-    private String loadImplementationClassName(ClassLoader classLoader) {
+    private String loadImplementationClassName(ClassLoader classLoader, Distribution distribution) {
         try {
-            InputStream inputStream = classLoader.getResourceAsStream("META-INF/services/org.gradle.tooling.internal.protocol.GradleConnectionFactoryVersion1");
+            String resourceName = "META-INF/services/" + ConnectionVersion4.class.getName();
+            InputStream inputStream = classLoader.getResourceAsStream(resourceName);
+            if (inputStream == null) {
+                Matcher m = Pattern.compile("\\w+Version(\\d+)").matcher(ConnectionVersion4.class.getSimpleName());
+                m.matches();
+                String protocolVersion = m.group(1);
+                throw new UnsupportedVersionException(String.format("The specified %s is not supported by this tooling API version (%s, protocol version %s)", distribution.getDisplayName(), GradleVersion.current().getVersion(), protocolVersion));
+            }
+
             try {
                 BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
                 String line;
@@ -65,7 +89,9 @@ public class DefaultToolingImplementationLoader implements ToolingImplementation
             } finally {
                 inputStream.close();
             }
-            throw new UnsupportedOperationException();
+            throw new UnsupportedOperationException(String.format("No implementation class specified in resource '%s'.", resourceName));
+        } catch (UnsupportedVersionException e) {
+            throw e;
         } catch (Throwable t) {
             throw new GradleConnectionException(String.format("Could not determine class name of Tooling API implementation."), t);
         }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/Distribution.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/Distribution.java
index 5a6af09..37504d6 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/Distribution.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/Distribution.java
@@ -19,5 +19,7 @@ import java.io.File;
 import java.util.Set;
 
 public interface Distribution {
+    String getDisplayName();
+
     Set<File> getToolingImplementationClasspath();
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DistributionFactory.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DistributionFactory.java
index 2af9bc2..d6ffae0 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DistributionFactory.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DistributionFactory.java
@@ -15,78 +15,172 @@
  */
 package org.gradle.tooling.internal.consumer;
 
-import org.gradle.StartParameter;
 import org.gradle.api.internal.DefaultClassPathProvider;
-import org.gradle.api.tasks.wrapper.Wrapper;
-import org.gradle.api.tasks.wrapper.internal.DistributionLocator;
+import org.gradle.logging.ProgressLogger;
+import org.gradle.logging.ProgressLoggerFactory;
 import org.gradle.tooling.GradleConnectionException;
+import org.gradle.util.DistributionLocator;
 import org.gradle.util.GradleVersion;
 import org.gradle.util.UncheckedException;
-import org.gradle.wrapper.Download;
-import org.gradle.wrapper.Install;
-import org.gradle.wrapper.PathAssembler;
+import org.gradle.wrapper.*;
 
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.LinkedHashSet;
 import java.util.Set;
 
 public class DistributionFactory {
-    public static final String USE_CLASSPATH_AS_DISTRIBUTION = "org.gradle.useClasspathAsDistribution";
-
-    public Distribution getCurrentDistribution() {
-        if ("true".equalsIgnoreCase(System.getProperty(USE_CLASSPATH_AS_DISTRIBUTION))) {
-            return new Distribution() {
-                public Set<File> getToolingImplementationClasspath() {
-                    DefaultClassPathProvider provider = new DefaultClassPathProvider();
-                    return provider.findClassPath("GRADLE_RUNTIME");
-                }
-            };
-        }
+    private final File userHomeDir;
+    private final ProgressLoggerFactory progressLoggerFactory;
 
-        return getDownloadedDistribution(new GradleVersion().getVersion());
+    public DistributionFactory(File userHomeDir, ProgressLoggerFactory progressLoggerFactory) {
+        this.userHomeDir = userHomeDir;
+        this.progressLoggerFactory = progressLoggerFactory;
     }
 
-    public Distribution getDistribution(final File gradleHomeDir) {
-        return new Distribution() {
-            public Set<File> getToolingImplementationClasspath() {
-                Set<File> files = new LinkedHashSet<File>();
-                File libDir = new File(gradleHomeDir, "lib");
-                for (File file : libDir.listFiles()) {
-                    if (file.getName().endsWith(".jar")) {
-                        files.add(file);
-                    }
-                }
-                return files;
-            }
-        };
+    /**
+     * Returns the default distribution to use for the specified project.
+     */
+    public Distribution getDefaultDistribution(File projectDir) {
+        Wrapper wrapper = new Wrapper(projectDir);
+        if (wrapper.getDistribution() != null) {
+            return getDistribution(wrapper.getDistribution());
+        }
+        return getDownloadedDistribution(GradleVersion.current().getVersion());
+    }
+
+    /**
+     * Returns the distribution installed in the specified directory.
+     */
+    public Distribution getDistribution(File gradleHomeDir) {
+        return new InstalledDistribution(gradleHomeDir, String.format("Gradle installation '%s'", gradleHomeDir), String.format("Gradle installation directory '%s'", gradleHomeDir));
     }
 
+    /**
+     * Returns the distribution for the specified gradle version.
+     */
     public Distribution getDistribution(String gradleVersion) {
-        if (gradleVersion.equals(new GradleVersion().getVersion())) {
-            return getCurrentDistribution();
-        }
         return getDownloadedDistribution(gradleVersion);
     }
 
+    /**
+     * Returns the distribution at the given URI.
+     */
+    public Distribution getDistribution(URI gradleDistribution) {
+        return new ZippedDistribution(gradleDistribution);
+    }
+
+    /**
+     * Uses the classpath to locate the distribution.
+     */
+    public Distribution getClasspathDistribution() {
+        return new ClasspathDistribution();
+    }
+
     private Distribution getDownloadedDistribution(String gradleVersion) {
         URI distUri;
         try {
-            distUri = new URI(new DistributionLocator().getDistributionFor(new GradleVersion(gradleVersion)));
+            distUri = new URI(new DistributionLocator().getDistributionFor(GradleVersion.version(gradleVersion)));
         } catch (URISyntaxException e) {
             throw UncheckedException.asUncheckedException(e);
         }
         return getDistribution(distUri);
     }
 
-    public Distribution getDistribution(URI gradleDistribution) {
-        try {
-            Install install = new Install(false, false, new Download(), new PathAssembler(StartParameter.DEFAULT_GRADLE_USER_HOME));
-            File installDir = install.createDist(gradleDistribution, PathAssembler.GRADLE_USER_HOME_STRING, Wrapper.DEFAULT_DISTRIBUTION_PARENT_NAME, PathAssembler.GRADLE_USER_HOME_STRING, Wrapper.DEFAULT_DISTRIBUTION_PARENT_NAME);
-            return getDistribution(installDir);
-        } catch (Exception e) {
-            throw new GradleConnectionException(String.format("Could not install Gradle distribution from '%s'.", gradleDistribution), e);
+    private class ZippedDistribution implements Distribution {
+        private final URI gradleDistribution;
+        private InstalledDistribution installedDistribution;
+
+        private ZippedDistribution(URI gradleDistribution) {
+            this.gradleDistribution = gradleDistribution;
+        }
+
+        public String getDisplayName() {
+            return String.format("Gradle distribution '%s'", gradleDistribution);
+        }
+
+        public Set<File> getToolingImplementationClasspath() {
+            if (installedDistribution == null) {
+                File installDir;
+                try {
+                    Install install = new Install(false, false, new ProgressReportingDownload(progressLoggerFactory), new PathAssembler(userHomeDir));
+                    installDir = install.createDist(gradleDistribution, PathAssembler.GRADLE_USER_HOME_STRING, org.gradle.api.tasks.wrapper.Wrapper.DEFAULT_DISTRIBUTION_PARENT_NAME, PathAssembler.GRADLE_USER_HOME_STRING, org.gradle.api.tasks.wrapper.Wrapper.DEFAULT_DISTRIBUTION_PARENT_NAME);
+                } catch (FileNotFoundException e) {
+                    throw new IllegalArgumentException(String.format("The specified %s does not exist.", getDisplayName()), e);
+                } catch (Exception e) {
+                    throw new GradleConnectionException(String.format("Could not install Gradle distribution from '%s'.", gradleDistribution), e);
+                }
+                installedDistribution = new InstalledDistribution(installDir, getDisplayName(), getDisplayName());
+            }
+            return installedDistribution.getToolingImplementationClasspath();
+        }
+    }
+
+    private static class ProgressReportingDownload implements IDownload {
+        private final ProgressLoggerFactory progressLoggerFactory;
+
+        private ProgressReportingDownload(ProgressLoggerFactory progressLoggerFactory) {
+            this.progressLoggerFactory = progressLoggerFactory;
+        }
+
+        public void download(URI address, File destination) throws Exception {
+            ProgressLogger progressLogger = progressLoggerFactory.newOperation(DistributionFactory.class);
+            progressLogger.setDescription(String.format("Download %s", address));
+            progressLogger.started();
+            try {
+                new Download().download(address, destination);
+            } finally {
+                progressLogger.completed();
+            }
+        }
+    }
+
+    private static class InstalledDistribution implements Distribution {
+        private final File gradleHomeDir;
+        private final String displayName;
+        private final String locationDisplayName;
+
+        public InstalledDistribution(File gradleHomeDir, String displayName, String locationDisplayName) {
+            this.gradleHomeDir = gradleHomeDir;
+            this.displayName = displayName;
+            this.locationDisplayName = locationDisplayName;
+        }
+
+        public String getDisplayName() {
+            return displayName;
+        }
+
+        public Set<File> getToolingImplementationClasspath() {
+            if (!gradleHomeDir.exists()) {
+                throw new IllegalArgumentException(String.format("The specified %s does not exist.", locationDisplayName));
+            }
+            if (!gradleHomeDir.isDirectory()) {
+                throw new IllegalArgumentException(String.format("The specified %s is not a directory.", locationDisplayName));
+            }
+            File libDir = new File(gradleHomeDir, "lib");
+            if (!libDir.isDirectory()) {
+                throw new IllegalArgumentException(String.format("The specified %s does not appear to contain a Gradle distribution.", locationDisplayName));
+            }
+            Set<File> files = new LinkedHashSet<File>();
+            for (File file : libDir.listFiles()) {
+                if (file.getName().endsWith(".jar")) {
+                    files.add(file);
+                }
+            }
+            return files;
+        }
+    }
+
+    private static class ClasspathDistribution implements Distribution {
+        public String getDisplayName() {
+            return "Gradle classpath distribution";
+        }
+
+        public Set<File> getToolingImplementationClasspath() {
+            DefaultClassPathProvider provider = new DefaultClassPathProvider();
+            return provider.findClassPath("GRADLE_RUNTIME");
         }
     }
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/LazyConnection.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/LazyConnection.java
new file mode 100644
index 0000000..ffa0d58
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/LazyConnection.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer;
+
+import org.gradle.tooling.internal.protocol.*;
+import org.gradle.util.UncheckedException;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * A {@link ConnectionVersion4} implementation which creates the actual connection implementation on demand.
+ */
+public class LazyConnection implements ConnectionVersion4 {
+    private final Distribution distribution;
+    private final ToolingImplementationLoader implementationLoader;
+    private final Lock lock = new ReentrantLock();
+    private final Condition condition = lock.newCondition();
+    private Set<Thread> executing = new HashSet<Thread>();
+    private boolean stopped;
+    private ConnectionVersion4 connection;
+
+    public LazyConnection(Distribution distribution, ToolingImplementationLoader implementationLoader) {
+        this.distribution = distribution;
+        this.implementationLoader = implementationLoader;
+    }
+
+    public void stop() {
+        ConnectionVersion4 connection = null;
+        lock.lock();
+        try {
+            stopped = true;
+            while (!executing.isEmpty()) {
+                try {
+                    condition.await();
+                } catch (InterruptedException e) {
+                    throw UncheckedException.asUncheckedException(e);
+                }
+            }
+            connection = this.connection;
+            this.connection = null;
+        } finally {
+            lock.unlock();
+        }
+        if (connection != null) {
+            connection.stop();
+        }
+    }
+
+    public ConnectionMetaDataVersion1 getMetaData() {
+        return new ConnectionMetaDataVersion1() {
+            public String getVersion() {
+                throw new UnsupportedOperationException();
+            }
+
+            public String getDisplayName() {
+                return distribution.getDisplayName();
+            }
+        };
+    }
+
+    public void executeBuild(final BuildParametersVersion1 buildParameters, final BuildOperationParametersVersion1 operationParameters) {
+        withConnection(new ConnectionAction<Object>() {
+            public Object run(ConnectionVersion4 connection) {
+                connection.executeBuild(buildParameters, operationParameters);
+                return null;
+            }
+        });
+    }
+
+    public ProjectVersion3 getModel(final Class<? extends ProjectVersion3> type, final BuildOperationParametersVersion1 operationParameters) {
+        return withConnection(new ConnectionAction<ProjectVersion3>() {
+            public ProjectVersion3 run(ConnectionVersion4 connection) {
+                return connection.getModel(type, operationParameters);
+            }
+        });
+    }
+
+    private <T> T withConnection(ConnectionAction<T> action) {
+        try {
+            ConnectionVersion4 connection = onStartAction();
+            return action.run(connection);
+        } finally {
+            onEndAction();
+        }
+    }
+
+    private ConnectionVersion4 onStartAction() {
+        lock.lock();
+        try {
+            if (stopped) {
+                throw new IllegalStateException("This connection has been stopped.");
+            }
+            executing.add(Thread.currentThread());
+            if (connection == null) {
+                // Hold the lock while creating the connection. Not generally good form.
+                // In this instance, blocks other threads from creating the connection at the same time
+                connection = implementationLoader.create(distribution);
+            }
+            return connection;
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    private void onEndAction() {
+        lock.lock();
+        try {
+            executing.remove(Thread.currentThread());
+            condition.signalAll();
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    private interface ConnectionAction<T> {
+        T run(ConnectionVersion4 connection);
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ProgressListenerAdapter.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ProgressListenerAdapter.java
new file mode 100644
index 0000000..1f0d730
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ProgressListenerAdapter.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer;
+
+import org.gradle.listener.ListenerBroadcast;
+import org.gradle.tooling.ProgressEvent;
+import org.gradle.tooling.ProgressListener;
+import org.gradle.tooling.internal.protocol.ProgressListenerVersion1;
+
+import java.util.LinkedList;
+
+class ProgressListenerAdapter implements ProgressListenerVersion1 {
+    private final ListenerBroadcast<ProgressListener> listeners = new ListenerBroadcast<ProgressListener>(ProgressListener.class);
+    private final LinkedList<String> stack = new LinkedList<String>();
+
+    public void onOperationStart(final String description) {
+        stack.addFirst(description == null ? "" : description);
+        fireChangeEvent();
+    }
+
+    public void onOperationEnd() {
+        stack.removeFirst();
+        fireChangeEvent();
+    }
+
+    private void fireChangeEvent() {
+        final String description = stack.isEmpty() ? "" : stack.getFirst();
+        listeners.getSource().statusChanged(new ProgressEvent() {
+            public String getDescription() {
+                return description;
+            }
+        });
+    }
+
+    public void add(ProgressListener listener) {
+        listeners.add(listener);
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ProgressLoggingConnection.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ProgressLoggingConnection.java
new file mode 100644
index 0000000..ba59d9a
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ProgressLoggingConnection.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer;
+
+import org.gradle.listener.ListenerManager;
+import org.gradle.logging.ProgressLogger;
+import org.gradle.logging.ProgressLoggerFactory;
+import org.gradle.logging.internal.ProgressCompleteEvent;
+import org.gradle.logging.internal.ProgressEvent;
+import org.gradle.logging.internal.ProgressListener;
+import org.gradle.logging.internal.ProgressStartEvent;
+import org.gradle.tooling.internal.protocol.*;
+
+/**
+ * A {@link ConnectionVersion4} implementation which provides some high-level progress information.
+ */
+public class ProgressLoggingConnection implements ConnectionVersion4 {
+    private final ConnectionVersion4 connection;
+    private final ProgressLoggerFactory progressLoggerFactory;
+    private final ListenerManager listenerManager;
+
+    public ProgressLoggingConnection(ConnectionVersion4 connection, ProgressLoggerFactory progressLoggerFactory, ListenerManager listenerManager) {
+        this.connection = connection;
+        this.progressLoggerFactory = progressLoggerFactory;
+        this.listenerManager = listenerManager;
+    }
+
+    public void stop() {
+        connection.stop();
+    }
+
+    public ConnectionMetaDataVersion1 getMetaData() {
+        return connection.getMetaData();
+    }
+
+    public void executeBuild(final BuildParametersVersion1 buildParameters, final BuildOperationParametersVersion1 operationParameters) {
+        run("Execute build", operationParameters, new BuildAction<Void>() {
+            public Void run(ConnectionVersion4 connection) {
+                connection.executeBuild(buildParameters, operationParameters);
+                return null;
+            }
+        });
+    }
+
+    public ProjectVersion3 getModel(final Class<? extends ProjectVersion3> type, final BuildOperationParametersVersion1 operationParameters) {
+        return run("Load projects", operationParameters, new BuildAction<ProjectVersion3>() {
+            public ProjectVersion3 run(ConnectionVersion4 connection) {
+                return connection.getModel(type, operationParameters);
+            }
+        });
+    }
+
+    private <T> T run(String description, BuildOperationParametersVersion1 parameters, BuildAction<T> action) {
+        ProgressListenerAdapter listener = new ProgressListenerAdapter(parameters.getProgressListener());
+        listenerManager.addListener(listener);
+        try {
+            ProgressLogger progressLogger = progressLoggerFactory.newOperation(ProgressLoggingConnection.class);
+            progressLogger.setDescription(description);
+            progressLogger.started();
+            try {
+                return action.run(connection);
+            } finally {
+                progressLogger.completed();
+            }
+        } finally {
+            listenerManager.removeListener(listener);
+        }
+    }
+
+    private interface BuildAction<T> {
+        T run(ConnectionVersion4 connection);
+    }
+
+    private static class ProgressListenerAdapter implements ProgressListener {
+        private final ProgressListenerVersion1 progressListener;
+
+        public ProgressListenerAdapter(ProgressListenerVersion1 progressListener) {
+            this.progressListener = progressListener;
+        }
+
+        public void started(ProgressStartEvent event) {
+            progressListener.onOperationStart(event.getDescription());
+        }
+
+        public void progress(ProgressEvent event) {
+        }
+
+        public void completed(ProgressCompleteEvent event) {
+            progressListener.onOperationEnd();
+        }
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ProtocolToModelAdapter.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ProtocolToModelAdapter.java
index 86caa4f..612e274 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ProtocolToModelAdapter.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ProtocolToModelAdapter.java
@@ -86,14 +86,20 @@ public class ProtocolToModelAdapter {
             return doInvokeMethod(method, params);
         }
 
-        private Object doInvokeMethod(Method method, Object[] params) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+        private Object doInvokeMethod(Method method, Object[] params) throws Throwable {
             Method targetMethod = methods.get(method);
             if (targetMethod == null) {
                 targetMethod = findMethod(method);
                 methods.put(method, targetMethod);
             }
 
-            Object returnValue = targetMethod.invoke(delegate, params);
+            Object returnValue;
+            try {
+                returnValue = targetMethod.invoke(delegate, params);
+            } catch (InvocationTargetException e) {
+                throw e.getCause();
+            }
+
             if (returnValue == null || method.getReturnType().isInstance(returnValue)) {
                 return returnValue;
             }
@@ -101,8 +107,14 @@ public class ProtocolToModelAdapter {
             return convert(returnValue, method.getGenericReturnType());
         }
 
-        private Method findMethod(Method method) throws NoSuchMethodException {
-            Method match = delegate.getClass().getMethod(method.getName(), method.getParameterTypes());
+        private Method findMethod(Method method) {
+            Method match;
+            try {
+                match = delegate.getClass().getMethod(method.getName(), method.getParameterTypes());
+            } catch (NoSuchMethodException e) {
+                throw new UnsupportedOperationException(String.format("Cannot map method %s.%s() to target object of type %s.", method.getDeclaringClass().getSimpleName(), method.getName(), delegate.getClass().getSimpleName()), e);
+            }
+
             LinkedList<Class<?>> queue = new LinkedList<Class<?>>();
             queue.add(delegate.getClass());
             while (!queue.isEmpty()) {
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ResultHandlerAdapter.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ResultHandlerAdapter.java
new file mode 100644
index 0000000..413893f
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ResultHandlerAdapter.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer;
+
+import org.gradle.tooling.BuildException;
+import org.gradle.tooling.GradleConnectionException;
+import org.gradle.tooling.ResultHandler;
+import org.gradle.tooling.internal.protocol.BuildExceptionVersion1;
+import org.gradle.tooling.internal.protocol.ResultHandlerVersion1;
+
+/**
+ * Adapts a {@link ResultHandler} to a {@link ResultHandlerVersion1}.
+ *
+ * @param <T> The result type.
+ */
+abstract class ResultHandlerAdapter<T> implements ResultHandlerVersion1<T> {
+    private final ResultHandler<? super T> handler;
+
+    ResultHandlerAdapter(ResultHandler<? super T> handler) {
+        this.handler = handler;
+    }
+
+    public void onComplete(T result) {
+        handler.onComplete(result);
+    }
+
+    public void onFailure(Throwable failure) {
+        if (failure instanceof GradleConnectionException) {
+            handler.onFailure((GradleConnectionException) failure);
+        } else if (failure instanceof BuildExceptionVersion1) {
+            handler.onFailure(new BuildException(connectionFailureMessage(failure), failure.getCause()));
+        } else {
+            handler.onFailure(new GradleConnectionException(connectionFailureMessage(failure), failure));
+        }
+    }
+
+    protected abstract String connectionFailureMessage(Throwable failure);
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ToolingImplementationLoader.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ToolingImplementationLoader.java
index 9a9fd95..910cedc 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ToolingImplementationLoader.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ToolingImplementationLoader.java
@@ -15,8 +15,8 @@
  */
 package org.gradle.tooling.internal.consumer;
 
-import org.gradle.tooling.internal.protocol.ConnectionFactoryVersion1;
+import org.gradle.tooling.internal.protocol.ConnectionVersion4;
 
 public interface ToolingImplementationLoader {
-    ConnectionFactoryVersion1 create(Distribution distribution);
+    ConnectionVersion4 create(Distribution distribution);
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/BuildExceptionVersion1.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/BuildExceptionVersion1.java
new file mode 100644
index 0000000..029b9e1
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/BuildExceptionVersion1.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.protocol;
+
+/**
+ * A wrapper around a build failure, to distinguish it from an infrastructure failure.
+ *
+ * DO NOT CHANGE THIS CLASS. It is part of the cross-version protocol.
+ */
+public class BuildExceptionVersion1 extends RuntimeException {
+    public BuildExceptionVersion1(Throwable throwable) {
+        super(throwable);
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/BuildOperationParametersVersion1.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/BuildOperationParametersVersion1.java
new file mode 100644
index 0000000..4955b20
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/BuildOperationParametersVersion1.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.protocol;
+
+import java.io.File;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
+ */
+public interface BuildOperationParametersVersion1 extends LongRunningOperationParametersVersion1 {
+    File getProjectDir();
+
+    /**
+     * Specifies whether to search for root project, or null to use default.
+     */
+    Boolean isSearchUpwards();
+
+    /**
+     * Returns the Gradle user home directory, or null to use default.
+     */
+    File getGradleUserHomeDir();
+
+    /**
+     * Specifies whether to run the build in this process, or null to use default.
+     */
+    Boolean isEmbedded();
+
+    /**
+     * Specifies the maximum idle time for any daemon process launched by the provider, or null to use the default.
+     */
+    Integer getDaemonMaxIdleTimeValue();
+
+    /**
+     * Specifies the units for the maximum idle time.
+     */
+    TimeUnit getDaemonMaxIdleTimeUnits();
+
+    long getStartTime();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/BuildParametersVersion1.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/BuildParametersVersion1.java
new file mode 100644
index 0000000..bd20a9e
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/BuildParametersVersion1.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.protocol;
+
+import java.util.List;
+
+/**
+ * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
+ */
+public interface BuildParametersVersion1 {
+    List<String> getTasks();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/BuildVersion1.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/BuildVersion1.java
deleted file mode 100644
index 10abfa5..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/BuildVersion1.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.tooling.internal.protocol;
-
-/**
- * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
- */
-public interface BuildVersion1 {
-    ProjectVersion1 getRootProject();
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/BuildableProjectVersion1.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/BuildableProjectVersion1.java
new file mode 100644
index 0000000..5f44c14
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/BuildableProjectVersion1.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.protocol;
+
+/**
+ * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
+ */
+public interface BuildableProjectVersion1 extends ProjectVersion3 {
+    Iterable<? extends TaskVersion1> getTasks();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ConnectionFactoryVersion1.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ConnectionFactoryVersion1.java
deleted file mode 100644
index 20edaac..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ConnectionFactoryVersion1.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.tooling.internal.protocol;
-
-import java.io.File;
-
-/**
- * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
- */
-public interface ConnectionFactoryVersion1 {
-    /**
-     * Stops this factory, blocking until complete.
-     */
-    void stop();
-
-    ConnectionVersion1 create(File projectDirectory);
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ConnectionMetaDataVersion1.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ConnectionMetaDataVersion1.java
new file mode 100644
index 0000000..295afb6
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ConnectionMetaDataVersion1.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.protocol;
+
+public interface ConnectionMetaDataVersion1 {
+    /**
+     * Returns the Gradle version for the connection.
+     */
+    String getVersion();
+
+    /**
+     * Returns a display name for the connection, which can be used in logging and error reporting.
+     */
+    String getDisplayName();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ConnectionVersion1.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ConnectionVersion1.java
deleted file mode 100644
index 012e01d..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ConnectionVersion1.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.tooling.internal.protocol;
-
-/**
- * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
- */
-public interface ConnectionVersion1 {
-    /**
-     * Returns a display name for this connection, which can be used in logging and error reporting.
-     *
-     * @return The display name.
-     */
-    String getDisplayName();
-
-    /**
-     * Fetches a snapshot of the model for the build. This method returns immediately.
-     *
-     * @param type The type of model to fetch.
-     * @param handler The handler to pass the model to.
-     * @param <T> The type of model to fetch.
-     * @throws UnsupportedOperationException When the given model type is not supported.
-     */
-    <T extends BuildVersion1> void getModel(Class<T> type, ResultHandlerVersion1<? super T> handler) throws UnsupportedOperationException;
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ConnectionVersion4.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ConnectionVersion4.java
new file mode 100644
index 0000000..2f52a96
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ConnectionVersion4.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.protocol;
+
+/**
+ * <p>Represents a connection to a particular Gradle implementation.
+ *
+ * <p>Implementations must be thread-safe.
+ *
+ * <p>DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.</p>
+ */
+public interface ConnectionVersion4 {
+    /**
+     * Stops this connection, blocking until complete.
+     */
+    void stop();
+
+    /**
+     * Returns the meta-data for this connection. The implementation of this method should be fast, and should continue to work after the connection has been stopped.
+     * @return The meta-data.
+     */
+    ConnectionMetaDataVersion1 getMetaData();
+
+    /**
+     * Fetches a snapshot of the model for the project.
+     *
+     * @throws UnsupportedOperationException When the given model type is not supported.
+     * @throws IllegalStateException When this connection has been stopped.
+     */
+    ProjectVersion3 getModel(Class<? extends ProjectVersion3> type, BuildOperationParametersVersion1 operationParameters) throws UnsupportedOperationException, IllegalStateException;
+
+    /**
+     * Executes a build.
+     *
+     * @param buildParameters The parameters for the build.
+     * @throws IllegalStateException When this connection has been stopped.
+     */
+    void executeBuild(BuildParametersVersion1 buildParameters, BuildOperationParametersVersion1 operationParameters) throws IllegalStateException;
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ExternalDependencyVersion1.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ExternalDependencyVersion1.java
index 09f6370..46f897a 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ExternalDependencyVersion1.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ExternalDependencyVersion1.java
@@ -15,8 +15,13 @@
  */
 package org.gradle.tooling.internal.protocol;
 
+import java.io.File;
+
 /**
  * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
  */
 public interface ExternalDependencyVersion1 extends DependencyVersion1 {
+    File getFile();
+    File getSource();
+    File getJavadoc();
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/HierarchicalProjectVersion1.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/HierarchicalProjectVersion1.java
new file mode 100644
index 0000000..84fea62
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/HierarchicalProjectVersion1.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.protocol;
+
+/**
+ * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
+ */
+public interface HierarchicalProjectVersion1 extends ProjectVersion3 {
+    HierarchicalProjectVersion1 getParent();
+
+    Iterable<? extends HierarchicalProjectVersion1> getChildren();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/LongRunningOperationParametersVersion1.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/LongRunningOperationParametersVersion1.java
new file mode 100644
index 0000000..5bf5da8
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/LongRunningOperationParametersVersion1.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.protocol;
+
+import java.io.OutputStream;
+
+/**
+ * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
+ */
+public interface LongRunningOperationParametersVersion1 {
+    /**
+     * Returns the output stream to write stdout logging to.
+     *
+     * @return The output stream. May be null.
+     */
+    OutputStream getStandardOutput();
+
+    /**
+     * Returns the output stream to write stderr logging to.
+     *
+     * @return The output stream. May be null.
+     */
+    OutputStream getStandardError();
+
+    /**
+     * Returns the listener to receive progress events.
+     *
+     * @return The listener. Must not be null.
+     */
+    ProgressListenerVersion1 getProgressListener();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ProgressListenerVersion1.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ProgressListenerVersion1.java
new file mode 100644
index 0000000..a91f8e8
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ProgressListenerVersion1.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.protocol;
+
+/**
+ * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
+ */
+public interface ProgressListenerVersion1 {
+    void onOperationStart(String description);
+
+    void onOperationEnd();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ProjectDependencyVersion1.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ProjectDependencyVersion1.java
new file mode 100644
index 0000000..2d56b63
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ProjectDependencyVersion1.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.protocol;
+
+/**
+ * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
+ */
+public interface ProjectDependencyVersion1 {
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ProjectVersion1.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ProjectVersion1.java
deleted file mode 100644
index 25de4d0..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ProjectVersion1.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.tooling.internal.protocol;
-
-/**
- * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
- */
-public interface ProjectVersion1 {
-    String getName();
-
-    Iterable<? extends ProjectVersion1> getChildProjects();
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ProjectVersion3.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ProjectVersion3.java
new file mode 100644
index 0000000..8a9378e
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ProjectVersion3.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.protocol;
+
+import java.io.File;
+
+/**
+ * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
+ */
+public interface ProjectVersion3 {
+    String getPath();
+
+    String getName();
+
+    String getDescription();
+
+    File getProjectDirectory();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/TaskVersion1.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/TaskVersion1.java
new file mode 100644
index 0000000..eb3722d
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/TaskVersion1.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.protocol;
+
+/**
+ * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
+ */
+public interface TaskVersion1 {
+    String getPath();
+
+    String getName();
+
+    String getDescription();
+
+    ProjectVersion3 getProject();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/EclipseBuildVersion1.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/EclipseBuildVersion1.java
deleted file mode 100644
index ae90b8d..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/EclipseBuildVersion1.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.tooling.internal.protocol.eclipse;
-
-import org.gradle.tooling.internal.protocol.BuildVersion1;
-
-/**
- * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
- */
-public interface EclipseBuildVersion1 extends BuildVersion1 {
-    EclipseProjectVersion1 getRootProject();
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/EclipseProjectDependencyVersion2.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/EclipseProjectDependencyVersion2.java
new file mode 100644
index 0000000..7e905dd
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/EclipseProjectDependencyVersion2.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.protocol.eclipse;
+
+import org.gradle.tooling.internal.protocol.ProjectDependencyVersion1;
+
+/**
+ * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
+ */
+public interface EclipseProjectDependencyVersion2 extends ProjectDependencyVersion1 {
+    HierarchicalEclipseProjectVersion1 getTargetProject();
+
+    String getPath();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/EclipseProjectVersion1.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/EclipseProjectVersion1.java
deleted file mode 100644
index 8cee416..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/EclipseProjectVersion1.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.tooling.internal.protocol.eclipse;
-
-import org.gradle.tooling.internal.protocol.ExternalDependencyVersion1;
-import org.gradle.tooling.internal.protocol.ProjectVersion1;
-
-/**
- * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
- */
-public interface EclipseProjectVersion1 extends ProjectVersion1 {
-    Iterable<? extends EclipseProjectVersion1> getChildProjects();
-
-    Iterable<? extends ExternalDependencyVersion1> getClasspath();
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/EclipseProjectVersion3.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/EclipseProjectVersion3.java
new file mode 100644
index 0000000..1dcfe30
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/EclipseProjectVersion3.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.protocol.eclipse;
+
+import org.gradle.tooling.internal.protocol.BuildableProjectVersion1;
+import org.gradle.tooling.internal.protocol.ExternalDependencyVersion1;
+
+/**
+ * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
+ */
+public interface EclipseProjectVersion3 extends HierarchicalEclipseProjectVersion1, BuildableProjectVersion1 {
+    EclipseProjectVersion3 getParent();
+
+    Iterable<? extends EclipseTaskVersion1> getTasks();
+
+    Iterable<? extends EclipseProjectVersion3> getChildren();
+
+    Iterable<? extends ExternalDependencyVersion1> getClasspath();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/EclipseSourceDirectoryVersion1.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/EclipseSourceDirectoryVersion1.java
new file mode 100644
index 0000000..a400d6b
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/EclipseSourceDirectoryVersion1.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.protocol.eclipse;
+
+import java.io.File;
+
+/**
+ * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
+ */
+public interface EclipseSourceDirectoryVersion1 {
+    File getDirectory();
+
+    String getPath();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/EclipseTaskVersion1.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/EclipseTaskVersion1.java
new file mode 100644
index 0000000..b9b35fe
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/EclipseTaskVersion1.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.protocol.eclipse;
+
+import org.gradle.tooling.internal.protocol.TaskVersion1;
+
+/**
+ * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
+ */
+public interface EclipseTaskVersion1 extends TaskVersion1 {
+    EclipseProjectVersion3 getProject();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/HierarchicalEclipseProjectVersion1.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/HierarchicalEclipseProjectVersion1.java
new file mode 100644
index 0000000..2b5d798
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/HierarchicalEclipseProjectVersion1.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.protocol.eclipse;
+
+import org.gradle.tooling.internal.protocol.HierarchicalProjectVersion1;
+
+/**
+ * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
+ */
+public interface HierarchicalEclipseProjectVersion1 extends HierarchicalProjectVersion1 {
+    HierarchicalEclipseProjectVersion1 getParent();
+
+    Iterable<? extends HierarchicalEclipseProjectVersion1> getChildren();
+
+    Iterable<? extends EclipseSourceDirectoryVersion1> getSourceDirectories();
+
+    Iterable<? extends EclipseProjectDependencyVersion2> getProjectDependencies();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/provider/DefaultConnection.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/provider/DefaultConnection.java
deleted file mode 100644
index 55f760a..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/provider/DefaultConnection.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.tooling.internal.provider;
-
-import org.gradle.BuildAdapter;
-import org.gradle.GradleLauncher;
-import org.gradle.StartParameter;
-import org.gradle.api.Project;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.invocation.Gradle;
-import org.gradle.messaging.actor.ActorFactory;
-import org.gradle.tooling.internal.protocol.BuildVersion1;
-import org.gradle.tooling.internal.protocol.ConnectionVersion1;
-import org.gradle.tooling.internal.protocol.ExternalDependencyVersion1;
-import org.gradle.tooling.internal.protocol.ResultHandlerVersion1;
-import org.gradle.tooling.internal.protocol.eclipse.EclipseBuildVersion1;
-import org.gradle.tooling.internal.protocol.eclipse.EclipseProjectVersion1;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-public class DefaultConnection implements ConnectionVersion1 {
-    private final File projectDir;
-    private final ActorFactory actorFactory;
-    private Worker worker;
-
-    public DefaultConnection(File projectDir, ActorFactory actorFactory) {
-        this.projectDir = projectDir;
-        this.actorFactory = actorFactory;
-    }
-
-    public String getDisplayName() {
-        return "Gradle connection";
-    }
-
-    public <T extends BuildVersion1> void getModel(Class<T> type, ResultHandlerVersion1<? super T> handler) throws UnsupportedOperationException {
-        if (worker == null) {
-            worker = actorFactory.createActor(new WorkerImpl()).getProxy(Worker.class);
-        }
-        worker.build(type, handler);
-    }
-
-    private static class ModelBuilder extends BuildAdapter {
-        private DefaultEclipseProject rootProject;
-
-        @Override
-        public void projectsEvaluated(Gradle gradle) {
-            rootProject = build(gradle.getRootProject());
-        }
-
-        private DefaultEclipseProject build(Project project) {
-            Configuration configuration = project.getConfigurations().findByName(
-                    "testRuntime");
-            List<ExternalDependencyVersion1> dependencies = new ArrayList<ExternalDependencyVersion1>();
-            if (configuration != null) {
-                Set<File> classpath = configuration.getFiles();
-                for (final File file : classpath) {
-                    dependencies.add(new ExternalDependencyVersion1() {
-                        public File getFile() {
-                            return file;
-                        }
-                    });
-                }
-            }
-            List<EclipseProjectVersion1> children = new ArrayList<EclipseProjectVersion1>();
-            for (Project child : project.getChildProjects().values()) {
-                children.add(build(child));
-            }
-
-            return new DefaultEclipseProject(project.getName(), children, dependencies);
-        }
-    }
-
-    private interface Worker {
-        <T extends BuildVersion1> void build(Class<T> type, ResultHandlerVersion1<? super T> handler);
-    }
-
-    private class WorkerImpl implements Worker {
-        public <T extends BuildVersion1> void build(Class<T> type, ResultHandlerVersion1<? super T> handler) {
-            try {
-                handler.onComplete(build(type));
-            } catch (Throwable t) {
-                handler.onFailure(t);
-            }
-        }
-
-        private <T extends BuildVersion1> T build(Class<T> type) throws UnsupportedOperationException {
-            if (type.isAssignableFrom(EclipseBuildVersion1.class)) {
-                StartParameter startParameter = new StartParameter();
-                startParameter.setProjectDir(projectDir);
-                startParameter.setSearchUpwards(false);
-
-                final GradleLauncher gradleLauncher = GradleLauncher.newInstance(startParameter);
-                final ModelBuilder builder = new ModelBuilder();
-                gradleLauncher.addListener(builder);
-                gradleLauncher.getBuildAnalysis().rethrowFailure();
-                return type.cast(new DefaultEclipseBuild(builder.rootProject));
-            }
-
-            throw new UnsupportedOperationException();
-        }
-    }
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/provider/DefaultConnectionFactory.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/provider/DefaultConnectionFactory.java
deleted file mode 100644
index ecb2972..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/provider/DefaultConnectionFactory.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.tooling.internal.provider;
-
-import org.gradle.messaging.actor.internal.DefaultActorFactory;
-import org.gradle.messaging.concurrent.CompositeStoppable;
-import org.gradle.messaging.concurrent.DefaultExecutorFactory;
-import org.gradle.tooling.internal.protocol.ConnectionFactoryVersion1;
-import org.gradle.tooling.internal.protocol.ConnectionVersion1;
-
-import java.io.File;
-
-/**
- * The implementation of the tooling API provider. This is loaded dynamically by the tooling API consumer, {@link org.gradle.tooling.internal.consumer.ConnectionFactory}.
- */
-public class DefaultConnectionFactory implements ConnectionFactoryVersion1 {
-    private final DefaultExecutorFactory executorFactory = new DefaultExecutorFactory();
-    private final DefaultActorFactory actorFactory = new DefaultActorFactory(executorFactory);
-
-    public void stop() {
-        new CompositeStoppable(actorFactory, executorFactory).stop();
-    }
-
-    public ConnectionVersion1 create(File projectDirectory) {
-        return new DefaultConnection(projectDirectory, actorFactory);
-    }
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/provider/DefaultEclipseBuild.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/provider/DefaultEclipseBuild.java
deleted file mode 100644
index c4bb71d..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/provider/DefaultEclipseBuild.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.tooling.internal.provider;
-
-import org.gradle.tooling.internal.protocol.eclipse.EclipseBuildVersion1;
-import org.gradle.tooling.internal.protocol.eclipse.EclipseProjectVersion1;
-
-class DefaultEclipseBuild implements EclipseBuildVersion1 {
-    private final EclipseProjectVersion1 rootProject;
-
-    public DefaultEclipseBuild(EclipseProjectVersion1 rootProject) {
-        this.rootProject = rootProject;
-    }
-
-    public EclipseProjectVersion1 getRootProject() {
-        return rootProject;
-    }
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/provider/DefaultEclipseProject.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/provider/DefaultEclipseProject.java
deleted file mode 100644
index 9859388..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/provider/DefaultEclipseProject.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.tooling.internal.provider;
-
-import org.gradle.tooling.internal.protocol.ExternalDependencyVersion1;
-import org.gradle.tooling.internal.protocol.eclipse.EclipseProjectVersion1;
-import org.gradle.util.GUtil;
-
-import java.util.List;
-
-class DefaultEclipseProject implements EclipseProjectVersion1 {
-    private final String name;
-    private final List<ExternalDependencyVersion1> classpath;
-    private final List<EclipseProjectVersion1> children;
-
-    public DefaultEclipseProject(String name, Iterable<? extends EclipseProjectVersion1> children, Iterable<ExternalDependencyVersion1> classpath) {
-        this.name = name;
-        this.children = GUtil.addLists(children);
-        this.classpath = GUtil.addLists(classpath);
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public List<EclipseProjectVersion1> getChildProjects() {
-        return children;
-    }
-
-    public List<ExternalDependencyVersion1> getClasspath() {
-        return classpath;
-    }
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/Build.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/Build.java
deleted file mode 100644
index 993b755..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/Build.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.tooling.model;
-
-/**
- * Represents a Gradle build.
- */
-public interface Build {
-    /**
-     * Returns the root project of this build.
-     *
-     * @return The root project.
-     */
-    Project getRootProject();
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/BuildableProject.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/BuildableProject.java
new file mode 100644
index 0000000..b59f90c
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/BuildableProject.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.model;
+
+/**
+ * Represents a project which has Gradle tasks associated with it.
+ */
+public interface BuildableProject extends Project {
+    /**
+     * Returns the tasks of this project.
+     *
+     * @return The tasks.
+     */
+    DomainObjectSet<? extends Task> getTasks();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/ExternalDependency.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/ExternalDependency.java
index 4936b50..4783ae1 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/ExternalDependency.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/ExternalDependency.java
@@ -27,4 +27,18 @@ public interface ExternalDependency extends Dependency {
      * @return The file. Never null.
      */
     File getFile();
+
+    /**
+     * Returns the source directory/archive for this dependency.
+     *
+     * @return The source file. Returns null when the source is not available for this dependency.
+     */
+    File getSource();
+
+    /**
+     * Returns the Javadoc directory/archive for this dependency.
+     *
+     * @return The Javadoc file. Returns null when the Javadoc is not available for this dependency.
+     */
+    File getJavadoc();
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/HierarchicalProject.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/HierarchicalProject.java
new file mode 100644
index 0000000..08ee969
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/HierarchicalProject.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.model;
+
+/**
+ * Represents a project which belongs to some hierarchy.
+ */
+public interface HierarchicalProject extends Project {
+    /**
+     * Returns the parent project of this project, if any.
+     *
+     * @return The parent, or null if this project has no parent.
+     */
+    HierarchicalProject getParent();
+
+    /**
+     * Returns the child projects of this project.
+     *
+     * @return The child projects. Returns an empty set if this project has no children.
+     */
+    DomainObjectSet<? extends HierarchicalProject> getChildren();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/Project.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/Project.java
index 1c948cf..b87d22a 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/Project.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/Project.java
@@ -15,21 +15,37 @@
  */
 package org.gradle.tooling.model;
 
+import java.io.File;
+
 /**
- * Represents a Gradle project.
+ * Represents a project of some kind.
  */
 public interface Project {
     /**
-     * Returns the name of this project.
+     * Returns the fully-qualified path of this project. This is a unique identifier for the project.
+     *
+     * @return The path.
+     */
+    String getPath();
+
+    /**
+     * Returns the name of this project. Note that the name is not a unique identifier for the project.
      *
      * @return The name.
      */
     String getName();
 
     /**
-     * Returns the child projects of this project.
+     * Returns the description of this project.
+     *
+     * @return The description. May be null.
+     */
+    String getDescription();
+
+    /**
+     * Returns the project directory for this project.
      *
-     * @return The child projects. Returns an empty set if this project has no children.
+     * @return The project directory.
      */
-    DomainObjectSet<? extends Project> getChildProjects();
+    File getProjectDirectory();
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/ProjectDependency.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/ProjectDependency.java
new file mode 100644
index 0000000..def051b
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/ProjectDependency.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.model;
+
+/**
+ * Represents a dependency on another project.
+ */
+public interface ProjectDependency extends Dependency {
+    /**
+     * Returns the target of this dependency.
+     *
+     * @return The target project. Does not return null.
+     */
+    Project getTargetProject();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/SourceDirectory.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/SourceDirectory.java
new file mode 100644
index 0000000..e523d8e
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/SourceDirectory.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.model;
+
+import java.io.File;
+
+/**
+ * Represents a source directory.
+ */
+public interface SourceDirectory {
+    /**
+     * Returns the source directory.
+     *
+     * @return The directory. Does not return null.
+     */
+    File getDirectory();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/Task.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/Task.java
new file mode 100644
index 0000000..043e77e
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/Task.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.model;
+
+/**
+ * Represents a task which is executable by Gradle.
+ */
+public interface Task {
+    /**
+     * Returns the path of this task. This is a fully qualified unique name for this task.
+     */
+    String getPath();
+
+    /**
+     * Returns the name of this task. Note that the name is not necessarily a unique identifier for the task.
+     *
+     * @return The name.
+     */
+    String getName();
+
+    /**
+     * Returns the description of this task.
+     *
+     * @return The description. May be null.
+     */
+    String getDescription();
+
+    /**
+     * Returns the project which this task belongs to.
+     *
+     * @return The project.
+     */
+    Project getProject();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/EclipseBuild.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/EclipseBuild.java
deleted file mode 100644
index b23c298..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/EclipseBuild.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.tooling.model.eclipse;
-
-import org.gradle.tooling.model.Build;
-
-/**
- * An Eclipse-centric view of a Gradle build.
- */
-public interface EclipseBuild extends Build {
-    /**
-     * {@inheritDoc}
-     */
-    EclipseProject getRootProject();
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/EclipseProject.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/EclipseProject.java
index 74437a7..ec08acd 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/EclipseProject.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/EclipseProject.java
@@ -15,23 +15,35 @@
  */
 package org.gradle.tooling.model.eclipse;
 
+import org.gradle.tooling.model.BuildableProject;
 import org.gradle.tooling.model.DomainObjectSet;
 import org.gradle.tooling.model.ExternalDependency;
-import org.gradle.tooling.model.Project;
 
 /**
- * An Eclipse-centric view of a Gradle project.
+ * The complete model of an Eclipse project.
+ *
+ * <p>Note that the names of Eclipse projects are unique, and can be used as an identifier for the project.
  */
-public interface EclipseProject extends Project {
+public interface EclipseProject extends HierarchicalEclipseProject, BuildableProject {
+    /**
+     * {@inheritDoc}
+     */
+    EclipseProject getParent();
+
+    /**
+     * {@inheritDoc}
+     */
+    DomainObjectSet<? extends EclipseProject> getChildren();
+
     /**
      * {@inheritDoc}
      */
-    DomainObjectSet<? extends EclipseProject> getChildProjects();
+    DomainObjectSet<? extends EclipseTask> getTasks();
 
     /**
-     * Returns the dependencies which make up the classpath of this project.
+     * Returns the external dependencies which make up the classpath of this project.
      *
-     * @return The dependencies. Returns an empty set if the project has no dependencies.
+     * @return The dependencies. Returns an empty set if the project has no external dependencies.
      */
     DomainObjectSet<? extends ExternalDependency> getClasspath();
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/EclipseProjectDependency.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/EclipseProjectDependency.java
new file mode 100644
index 0000000..da69715
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/EclipseProjectDependency.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.model.eclipse;
+
+import org.gradle.tooling.model.ProjectDependency;
+
+/**
+ * Represents a dependency on another Eclipse project.
+ */
+public interface EclipseProjectDependency extends ProjectDependency {
+    /**
+     * {@inheritDoc}
+     */
+    HierarchicalEclipseProject getTargetProject();
+
+    /**
+     * Returns the path to use for this project dependency.
+     */
+    String getPath();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/EclipseSourceDirectory.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/EclipseSourceDirectory.java
new file mode 100644
index 0000000..279e7b5
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/EclipseSourceDirectory.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.model.eclipse;
+
+import org.gradle.tooling.model.SourceDirectory;
+
+/**
+ * A source directory in an Eclipse project.
+ */
+public interface EclipseSourceDirectory extends SourceDirectory {
+    /**
+     * Returns the relative path for this source directory.
+     *
+     * @return The path for this source directory. Does not return null.
+     */
+    String getPath();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/EclipseTask.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/EclipseTask.java
new file mode 100644
index 0000000..93d15d5
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/EclipseTask.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.model.eclipse;
+
+import org.gradle.tooling.model.Task;
+
+/**
+ * An Eclipse centric view of a Gradle task.
+ */
+public interface EclipseTask extends Task {
+    /**
+     * {@inheritDoc}
+     */
+    EclipseProject getProject();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/HierarchicalEclipseProject.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/HierarchicalEclipseProject.java
new file mode 100644
index 0000000..62e5747
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/HierarchicalEclipseProject.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.model.eclipse;
+
+import org.gradle.tooling.model.DomainObjectSet;
+import org.gradle.tooling.model.HierarchicalProject;
+
+/**
+ * Represents the basic information about an Eclipse project.
+ */
+public interface HierarchicalEclipseProject extends HierarchicalProject {
+    /**
+     * {@inheritDoc}
+     */
+    HierarchicalEclipseProject getParent();
+
+    /**
+     * {@inheritDoc}
+     */
+    DomainObjectSet<? extends HierarchicalEclipseProject> getChildren();
+
+    /**
+     * Returns the project dependencies for this project.
+     *
+     * @return The project dependencies. Returns an empty set if the project has no project dependencies.
+     */
+    DomainObjectSet<? extends EclipseProjectDependency> getProjectDependencies();
+
+    /**
+     * Returns the source directories for this project.
+     *
+     * @return The source directories. Returns an empty set if the project has no source directories.
+     */
+    DomainObjectSet<? extends EclipseSourceDirectory> getSourceDirectories();
+}
diff --git a/subprojects/tooling-api/src/main/resources/META-INF/services/org.gradle.tooling.internal.protocol.GradleConnectionFactoryVersion1 b/subprojects/tooling-api/src/main/resources/META-INF/services/org.gradle.tooling.internal.protocol.GradleConnectionFactoryVersion1
deleted file mode 100644
index 11bb467..0000000
--- a/subprojects/tooling-api/src/main/resources/META-INF/services/org.gradle.tooling.internal.protocol.GradleConnectionFactoryVersion1
+++ /dev/null
@@ -1 +0,0 @@
-org.gradle.tooling.internal.provider.DefaultConnectionFactory
\ No newline at end of file
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/GradleConnectorTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/GradleConnectorTest.groovy
deleted file mode 100644
index dd5e5fa..0000000
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/GradleConnectorTest.groovy
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.tooling
-
-import spock.lang.Specification
-import org.gradle.tooling.internal.consumer.ConnectionFactory
-import org.gradle.tooling.internal.consumer.DistributionFactory
-import org.gradle.tooling.internal.consumer.Distribution
-
-class GradleConnectorTest extends Specification {
-    final ConnectionFactory connectionFactory = Mock()
-    final DistributionFactory distributionFactory = Mock()
-    final Distribution distribution = Mock()
-    final File projectDir = new File('project-dir')
-    final GradleConnector connector = new GradleConnector(connectionFactory, distributionFactory)
-
-    def canCreateAConnectionGivenAProjectDirectory() {
-        BuildConnection connection = Mock()
-
-        when:
-        def result = connector.forProjectDirectory(projectDir).connect()
-
-        then:
-        result == connection
-        1 * distributionFactory.currentDistribution >> distribution
-        1 * connectionFactory.create(distribution, projectDir) >> connection
-    }
-
-    def canSpecifyAGradleInstallationToUse() {
-        BuildConnection connection = Mock()
-        File gradleHome = new File('install-dir')
-
-        when:
-        def result = connector.useInstallation(gradleHome).forProjectDirectory(projectDir).connect()
-
-        then:
-        result == connection
-        1 * distributionFactory.getDistribution(gradleHome) >> distribution
-        1 * connectionFactory.create(distribution, projectDir) >> connection
-    }
-
-    def canSpecifyAGradleDistributionToUse() {
-        BuildConnection connection = Mock()
-        URI gradleDist = new URI('http://server/dist.zip')
-
-        when:
-        def result = connector.useDistribution(gradleDist).forProjectDirectory(projectDir).connect()
-
-        then:
-        result == connection
-        1 * distributionFactory.getDistribution(gradleDist) >> distribution
-        1 * connectionFactory.create(distribution, projectDir) >> connection
-    }
-
-    def canSpecifyAGradleVersionToUse() {
-        BuildConnection connection = Mock()
-
-        when:
-        def result = connector.useGradleVersion('1.0').forProjectDirectory(projectDir).connect()
-
-        then:
-        result == connection
-        1 * distributionFactory.getDistribution('1.0') >> distribution
-        1 * connectionFactory.create(distribution, projectDir) >> connection
-    }
-
-    def mustSpecifyAProjectDirectory() {
-        when:
-        connector.connect()
-
-        then:
-        IllegalStateException e = thrown()
-        e.message == 'A project directory must be specified before creating a connection.'
-    }
-
-    def stopsConnectionFactoryWhenClosed() {
-        when:
-        connector.close()
-
-        then:
-        1 * connectionFactory.stop()
-    }
-}
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/DefaultEclipseProjectTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/DefaultEclipseProjectTest.groovy
new file mode 100644
index 0000000..76fb812
--- /dev/null
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/DefaultEclipseProjectTest.groovy
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal
+
+import spock.lang.Specification
+
+class DefaultEclipseProjectTest extends Specification {
+    def usesPathForToStringValue() {
+        def project = new DefaultEclipseProject("name", ":path", null, null, [])
+
+        expect:
+        project.toString() == "project ':path'"
+    }
+}
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/CachingToolingImplementationLoaderTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/CachingToolingImplementationLoaderTest.groovy
index ae88671..b4e1bc6 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/CachingToolingImplementationLoaderTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/CachingToolingImplementationLoaderTest.groovy
@@ -15,29 +15,29 @@
  */
 package org.gradle.tooling.internal.consumer
 
+import org.gradle.tooling.internal.protocol.ConnectionVersion4
 import spock.lang.Specification
-import org.gradle.tooling.internal.protocol.ConnectionFactoryVersion1
 
 class CachingToolingImplementationLoaderTest extends Specification {
     final ToolingImplementationLoader target = Mock()
     final CachingToolingImplementationLoader loader = new CachingToolingImplementationLoader(target)
 
     def delegatesToTargetLoaderToCreateImplementation() {
-        ConnectionFactoryVersion1 factoryImpl = Mock()
+        ConnectionVersion4 connectionImpl = Mock()
         final Distribution distribution = Mock()
 
         when:
         def impl = loader.create(distribution)
 
         then:
-        impl == factoryImpl
-        1 * target.create(distribution) >> factoryImpl
+        impl == connectionImpl
+        1 * target.create(distribution) >> connectionImpl
         _ * distribution.toolingImplementationClasspath >> ([new File('a.jar')] as Set)
         0 * _._
     }
 
     def reusesImplementationWithSameClasspath() {
-        ConnectionFactoryVersion1 factoryImpl = Mock()
+        ConnectionVersion4 connectionImpl = Mock()
         final Distribution distribution = Mock()
 
         when:
@@ -45,16 +45,16 @@ class CachingToolingImplementationLoaderTest extends Specification {
         def impl2 = loader.create(distribution)
 
         then:
-        impl == factoryImpl
-        impl2 == factoryImpl
-        1 * target.create(distribution) >> factoryImpl
+        impl == connectionImpl
+        impl2 == connectionImpl
+        1 * target.create(distribution) >> connectionImpl
         _ * distribution.toolingImplementationClasspath >> ([new File('a.jar')] as Set)
         0 * _._
     }
 
     def createsNewImplementationWhenClasspathNotSeenBefore() {
-        ConnectionFactoryVersion1 factoryImpl1 = Mock()
-        ConnectionFactoryVersion1 factoryImpl2 = Mock()
+        ConnectionVersion4 connectionImpl1 = Mock()
+        ConnectionVersion4 connectionImpl2 = Mock()
         Distribution distribution1 = Mock()
         Distribution distribution2 = Mock()
 
@@ -63,10 +63,10 @@ class CachingToolingImplementationLoaderTest extends Specification {
         def impl2 = loader.create(distribution2)
 
         then:
-        impl == factoryImpl1
-        impl2 == factoryImpl2
-        1 * target.create(distribution1) >> factoryImpl1
-        1 * target.create(distribution2) >> factoryImpl2
+        impl == connectionImpl1
+        impl2 == connectionImpl2
+        1 * target.create(distribution1) >> connectionImpl1
+        1 * target.create(distribution2) >> connectionImpl2
         _ * distribution1.toolingImplementationClasspath >> ([new File('a.jar')] as Set)
         _ * distribution2.toolingImplementationClasspath >> ([new File('b.jar')] as Set)
         0 * _._
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/ConnectionFactoryTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/ConnectionFactoryTest.groovy
index f79ed90..181e59a 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/ConnectionFactoryTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/ConnectionFactoryTest.groovy
@@ -15,42 +15,29 @@
  */
 package org.gradle.tooling.internal.consumer
 
-import org.gradle.tooling.internal.protocol.ConnectionFactoryVersion1
-import org.gradle.tooling.internal.protocol.ConnectionVersion1
+import org.gradle.tooling.internal.protocol.ConnectionVersion4
 import spock.lang.Specification
+import org.gradle.listener.ListenerManager
+import org.gradle.logging.ProgressLoggerFactory
 
 class ConnectionFactoryTest extends Specification {
     final ToolingImplementationLoader implementationLoader = Mock()
+    final ListenerManager listenerManager = Mock()
+    final ProgressLoggerFactory progressLoggerFactory = Mock()
     final Distribution distribution = Mock()
-    final ConnectionFactoryVersion1 connectionImplFactory = Mock()
-    final ConnectionVersion1 connectionImpl = Mock()
-    final ConnectionFactory factory = new ConnectionFactory(implementationLoader)
+    final ConnectionVersion4 connectionImpl = Mock()
+    final ConnectionParameters parameters = Mock()
+    final ConnectionFactory factory = new ConnectionFactory(implementationLoader, listenerManager, progressLoggerFactory)
 
     def usesImplementationLoaderToLoadConnectionFactory() {
-        File projectDir = new File('project-dir')
-
         when:
-        factory.create(distribution, projectDir)
+        def result = factory.create(distribution, parameters)
 
         then:
-        1 * implementationLoader.create(distribution) >> connectionImplFactory
-        1 * connectionImplFactory.create(projectDir) >> connectionImpl
+        result instanceof DefaultProjectConnection
+        result.connection instanceof DefaultAsyncConnection
+        result.connection.connection instanceof ProgressLoggingConnection
+        result.connection.connection.connection instanceof LazyConnection
         0 * _._
     }
-
-    def stopsAllConnectionFactoriesOnStop() {
-        File projectDir = new File('project-dir')
-
-        when:
-        factory.create(distribution, projectDir)
-
-        then:
-        1 * implementationLoader.create(distribution) >> connectionImplFactory
-        
-        when:
-        factory.stop()
-
-        then:
-        1 * connectionImplFactory.stop()
-    }
 }
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultBuildConnectionTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultBuildConnectionTest.groovy
deleted file mode 100644
index cfc1791..0000000
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultBuildConnectionTest.groovy
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.tooling.internal.consumer
-
-import org.gradle.util.ConcurrentSpecification
-import org.gradle.tooling.internal.protocol.ConnectionVersion1
-import org.gradle.tooling.model.Build
-import org.gradle.tooling.ResultHandler
-import org.gradle.tooling.internal.protocol.BuildVersion1
-import org.gradle.tooling.internal.protocol.ResultHandlerVersion1
-import org.gradle.tooling.GradleConnectionException
-import org.gradle.tooling.UnsupportedVersionException
-
-class DefaultBuildConnectionTest extends ConcurrentSpecification {
-    final ConnectionVersion1 protocolConnection = Mock()
-    final ProtocolToModelAdapter adapter = Mock()
-    final DefaultBuildConnection connection = new DefaultBuildConnection(protocolConnection, adapter)
-
-    def getModelDelegatesToProtocolConnectionToFetchModel() {
-        ResultHandler<Build> handler = Mock()
-        ResultHandlerVersion1<BuildVersion1> adaptedHandler
-        BuildVersion1 result = Mock()
-        Build adaptedResult = Mock()
-
-        when:
-        connection.getModel(Build.class, handler)
-
-        then:
-        1 * protocolConnection.getModel(BuildVersion1.class, !null) >> {args -> adaptedHandler = args[1]}
-
-        when:
-        adaptedHandler.onComplete(result)
-
-        then:
-        1 * adapter.adapt(Build.class, result) >> adaptedResult
-        1 * handler.onComplete(adaptedResult)
-        0 * _._
-    }
-
-    def getModelWrapsFailureToFetchModel() {
-        ResultHandler<Build> handler = Mock()
-        ResultHandlerVersion1<BuildVersion1> adaptedHandler
-        RuntimeException failure = new RuntimeException()
-        GradleConnectionException wrappedFailure
-
-        when:
-        connection.getModel(Build.class, handler)
-
-        then:
-        1 * protocolConnection.getModel(BuildVersion1.class, !null) >> {args -> adaptedHandler = args[1]}
-
-        when:
-        adaptedHandler.onFailure(failure)
-
-        then:
-        1 * handler.onFailure(!null) >> {args -> wrappedFailure = args[0] }
-        _ * protocolConnection.displayName >> '[connection]'
-        wrappedFailure.message == 'Could not fetch model of type \'Build\' from [connection].'
-        wrappedFailure.cause.is(failure)
-        0 * _._
-    }
-
-    def getModelFailsForUnknownModelType() {
-        when:
-        connection.getModel(TestBuild.class)
-
-        then:
-        UnsupportedVersionException e = thrown()
-        e.message == 'Model of type \'TestBuild\' is not supported.'
-    }
-
-    def getModelBlocksUntilResultReceivedFromProtocolConnection() {
-        def supplyResult = later()
-        BuildVersion1 result = Mock()
-        Build adaptedResult = Mock()
-        _ * adapter.adapt(Build.class, result) >> adaptedResult
-
-        when:
-        def model
-        def action = start {
-            model = connection.getModel(Build.class)
-        }
-
-        then:
-        action.waitsFor(supplyResult)
-        1 * protocolConnection.getModel(BuildVersion1.class, !null) >> { args ->
-            def handler = args[1]
-            supplyResult.activate {
-                handler.onComplete(result)
-            }
-        }
-
-        when:
-        finished()
-
-        then:
-        model == adaptedResult
-    }
-
-    def getModelBlocksUntilFailureReceivedFromProtocolConnectionAndRethrowsFailure() {
-        def supplyResult = later()
-        RuntimeException failure = new RuntimeException()
-
-        when:
-        def model
-        def action = start {
-            model = connection.getModel(Build.class)
-        }
-
-        then:
-        action.waitsFor(supplyResult)
-        1 * protocolConnection.getModel(BuildVersion1.class, !null) >> { args ->
-            def handler = args[1]
-            supplyResult.activate {
-                handler.onFailure(failure)
-            }
-        }
-
-        when:
-        finished()
-
-        then:
-        GradleConnectionException e = thrown()
-        e.cause.is(failure)
-    }
-}
-
-interface TestBuild extends Build {
-    
-}
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultBuildLauncherTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultBuildLauncherTest.groovy
new file mode 100644
index 0000000..48d33b6
--- /dev/null
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultBuildLauncherTest.groovy
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer
+
+import org.gradle.tooling.GradleConnectionException
+import org.gradle.tooling.ResultHandler
+import org.gradle.tooling.model.Task
+import org.gradle.util.ConcurrentSpecification
+
+class DefaultBuildLauncherTest extends ConcurrentSpecification {
+    final AsyncConnection protocolConnection = Mock()
+    final ConnectionParameters parameters = Mock()
+    final DefaultBuildLauncher launcher = new DefaultBuildLauncher(protocolConnection, parameters)
+
+    def buildDelegatesToProtocolConnection() {
+        Task task1 = task(':task1')
+        Task task2 = task(':task2')
+        ResultHandler<Void> handler = Mock()
+
+        when:
+        launcher.forTasks(task1, task2).run(handler)
+
+        then:
+        1 * protocolConnection.executeBuild(!null, !null, !null) >> { args ->
+            def buildParams = args[0]
+            assert buildParams.tasks == [':task1', ':task2']
+            def params = args[1]
+            assert params.standardOutput == null
+            assert params.standardError == null
+            assert params.progressListener != null
+            def wrappedHandler = args[2]
+            wrappedHandler.onComplete(null)
+        }
+        1 * handler.onComplete(null)
+        0 * protocolConnection._
+        0 * handler._
+    }
+
+    def canRedirectStandardOutputAndError() {
+        ResultHandler<Void> handler = Mock()
+        OutputStream stdout = Mock()
+        OutputStream stderr = Mock()
+
+        when:
+        launcher.standardOutput = stdout
+        launcher.standardError = stderr
+        launcher.run(handler)
+
+        then:
+        1 * protocolConnection.executeBuild(!null, !null, !null) >> { args ->
+            def params = args[1]
+            assert params.standardOutput == stdout
+            assert params.standardError == stderr
+            def wrappedHandler = args[2]
+            wrappedHandler.onComplete(null)
+        }
+    }
+
+    def notifiesHandlerOnFailure() {
+        ResultHandler<Void> handler = Mock()
+        RuntimeException failure = new RuntimeException()
+
+        when:
+        launcher.run(handler)
+
+        then:
+        1 * protocolConnection.executeBuild(!null, !null, !null) >> { args ->
+            def wrappedHandler = args[2]
+            wrappedHandler.onFailure(failure)
+        }
+        1 * handler.onFailure(!null) >> { args ->
+            def e = args[0]
+            assert e instanceof GradleConnectionException
+            assert e.message == 'Could not execute build using <connection>.'
+        }
+        _ * protocolConnection.displayName >> '<connection>'
+        0 * protocolConnection._
+        0 * handler._
+    }
+
+    def buildBlocksUntilResultReceived() {
+        def supplyResult = later()
+        Task task = Mock()
+
+        when:
+        def action = start {
+            launcher.forTasks(task).run()
+        }
+
+        then:
+        action.waitsFor(supplyResult)
+        1 * protocolConnection.executeBuild(!null, !null, !null) >> { args ->
+            def handler = args[2]
+            supplyResult.finishLater {
+                handler.onComplete(null)
+            }
+        }
+    }
+
+    def buildBlocksUntilFailureReceived() {
+        def supplyResult = later()
+        Task task = Mock()
+
+        when:
+        def action = start {
+            launcher.forTasks(task).run()
+        }
+
+        then:
+        action.waitsFor(supplyResult)
+        1 * protocolConnection.executeBuild(!null, !null, !null) >> { args ->
+            def handler = args[2]
+            supplyResult.finishLater {
+                handler.onFailure(new RuntimeException())
+            }
+        }
+
+        when:
+        finished()
+
+        then:
+        GradleConnectionException e = thrown()
+    }
+
+    def task(String path) {
+        Task task = Mock()
+        _ * task.path >> path
+        return task
+    }
+}
+
+
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultGradleConnectorTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultGradleConnectorTest.groovy
new file mode 100644
index 0000000..feefc28
--- /dev/null
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultGradleConnectorTest.groovy
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer
+
+import org.gradle.tooling.GradleConnector
+import org.gradle.tooling.ProjectConnection
+import spock.lang.Specification
+
+class DefaultGradleConnectorTest extends Specification {
+    final ConnectionFactory connectionFactory = Mock()
+    final DistributionFactory distributionFactory = Mock()
+    final Distribution distribution = Mock()
+    final File projectDir = new File('project-dir')
+    final GradleConnector connector = new DefaultGradleConnector(connectionFactory, distributionFactory)
+
+    def canCreateAConnectionGivenAProjectDirectory() {
+        ProjectConnection connection = Mock()
+
+        when:
+        def result = connector.forProjectDirectory(projectDir).connect()
+
+        then:
+        result == connection
+        1 * distributionFactory.getDefaultDistribution(projectDir) >> distribution
+        1 * connectionFactory.create(distribution, { it.projectDir == projectDir }) >> connection
+    }
+
+    def canSpecifyUserHomeDir() {
+        ProjectConnection connection = Mock()
+        File userDir = new File('user-dir')
+
+        when:
+        def result = connector.useGradleUserHomeDir(userDir).forProjectDirectory(projectDir).connect()
+
+        then:
+        result == connection
+        1 * distributionFactory.getDefaultDistribution(projectDir) >> distribution
+        1 * connectionFactory.create(distribution, { it.gradleUserHomeDir == userDir }) >> connection
+    }
+
+    def canSpecifyAGradleInstallationToUse() {
+        ProjectConnection connection = Mock()
+        File gradleHome = new File('install-dir')
+
+        when:
+        def result = connector.useInstallation(gradleHome).forProjectDirectory(projectDir).connect()
+
+        then:
+        result == connection
+        1 * distributionFactory.getDistribution(gradleHome) >> distribution
+        1 * connectionFactory.create(distribution, !null) >> connection
+    }
+
+    def canSpecifyAGradleDistributionToUse() {
+        ProjectConnection connection = Mock()
+        URI gradleDist = new URI('http://server/dist.zip')
+
+        when:
+        def result = connector.useDistribution(gradleDist).forProjectDirectory(projectDir).connect()
+
+        then:
+        result == connection
+        1 * distributionFactory.getDistribution(gradleDist) >> distribution
+        1 * connectionFactory.create(distribution, !null) >> connection
+    }
+
+    def canSpecifyAGradleVersionToUse() {
+        ProjectConnection connection = Mock()
+
+        when:
+        def result = connector.useGradleVersion('1.0').forProjectDirectory(projectDir).connect()
+
+        then:
+        result == connection
+        1 * distributionFactory.getDistribution('1.0') >> distribution
+        1 * connectionFactory.create(distribution, !null) >> connection
+    }
+
+    def mustSpecifyAProjectDirectory() {
+        when:
+        connector.connect()
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'A project directory must be specified before creating a connection.'
+    }
+}
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultModelBuilderTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultModelBuilderTest.groovy
new file mode 100644
index 0000000..5e64b44
--- /dev/null
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultModelBuilderTest.groovy
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer
+
+import org.gradle.tooling.GradleConnectionException
+import org.gradle.tooling.ResultHandler
+import org.gradle.tooling.internal.protocol.ProjectVersion3
+import org.gradle.tooling.internal.protocol.ResultHandlerVersion1
+import org.gradle.tooling.model.Project
+import org.gradle.util.ConcurrentSpecification
+
+class DefaultModelBuilderTest extends ConcurrentSpecification {
+    final AsyncConnection protocolConnection = Mock()
+    final ProtocolToModelAdapter adapter = Mock()
+    final ConnectionParameters parameters = Mock()
+    final DefaultModelBuilder<Project> builder = new DefaultModelBuilder<Project>(Project, ProjectVersion3, protocolConnection, adapter, parameters)
+
+    def getModelDelegatesToProtocolConnectionToFetchModel() {
+        ResultHandler<Project> handler = Mock()
+        ResultHandlerVersion1<ProjectVersion3> adaptedHandler
+        ProjectVersion3 result = Mock()
+        Project adaptedResult = Mock()
+
+        when:
+        builder.get(handler)
+
+        then:
+        1 * protocolConnection.getModel(ProjectVersion3, !null, !null) >> {args ->
+            def params = args[1]
+            assert params.standardOutput == null
+            assert params.standardError == null
+            assert params.progressListener != null
+            adaptedHandler = args[2]
+        }
+
+        when:
+        adaptedHandler.onComplete(result)
+
+        then:
+        1 * adapter.adapt(Project.class, result) >> adaptedResult
+        1 * handler.onComplete(adaptedResult)
+        0 * _._
+    }
+
+    def getModelWrapsFailureToFetchModel() {
+        ResultHandler<Project> handler = Mock()
+        ResultHandlerVersion1<ProjectVersion3> adaptedHandler
+        RuntimeException failure = new RuntimeException()
+        GradleConnectionException wrappedFailure
+
+        when:
+        builder.get(handler)
+
+        then:
+        1 * protocolConnection.getModel(!null, !null, !null) >> {args -> adaptedHandler = args[2]}
+
+        when:
+        adaptedHandler.onFailure(failure)
+
+        then:
+        1 * handler.onFailure(!null) >> {args -> wrappedFailure = args[0] }
+        _ * protocolConnection.displayName >> '[connection]'
+        wrappedFailure.message == 'Could not fetch model of type \'Project\' using [connection].'
+        wrappedFailure.cause.is(failure)
+        0 * _._
+    }
+
+    def getModelBlocksUntilResultReceivedFromProtocolConnection() {
+        def supplyResult = later()
+        ProjectVersion3 result = Mock()
+        Project adaptedResult = Mock()
+        _ * adapter.adapt(Project.class, result) >> adaptedResult
+
+        when:
+        def model
+        def action = start {
+            model = builder.get()
+        }
+
+        then:
+        action.waitsFor(supplyResult)
+        1 * protocolConnection.getModel(!null, !null, !null) >> { args ->
+            def handler = args[2]
+            supplyResult.finishLater {
+                handler.onComplete(result)
+            }
+        }
+
+        when:
+        finished()
+
+        then:
+        model == adaptedResult
+    }
+
+    def getModelBlocksUntilFailureReceivedFromProtocolConnectionAndRethrowsFailure() {
+        def supplyResult = later()
+        RuntimeException failure = new RuntimeException()
+
+        when:
+        def model
+        def action = start {
+            model = builder.get()
+        }
+
+        then:
+        action.waitsFor(supplyResult)
+        1 * protocolConnection.getModel(!null, !null, !null) >> { args ->
+            def handler = args[2]
+            supplyResult.finishLater {
+                handler.onFailure(failure)
+            }
+        }
+
+        when:
+        finished()
+
+        then:
+        GradleConnectionException e = thrown()
+        e.cause.is(failure)
+    }
+}
+
+
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultProjectConnectionTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultProjectConnectionTest.groovy
new file mode 100644
index 0000000..9c0f67c
--- /dev/null
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultProjectConnectionTest.groovy
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer
+
+import org.gradle.tooling.UnsupportedVersionException
+import org.gradle.tooling.model.Project
+import org.gradle.util.ConcurrentSpecification
+
+class DefaultProjectConnectionTest extends ConcurrentSpecification {
+    final AsyncConnection protocolConnection = Mock()
+    final ProtocolToModelAdapter adapter = Mock()
+    final ConnectionParameters parameters = Mock()
+    final DefaultProjectConnection connection = new DefaultProjectConnection(protocolConnection, adapter, parameters)
+
+    def canCreateAModelBuilder() {
+        expect:
+        connection.model(Project.class) instanceof DefaultModelBuilder
+    }
+
+    def canCreateABuildLauncher() {
+        expect:
+        connection.newBuild() instanceof DefaultBuildLauncher
+    }
+    
+    def modelFailsForUnknownModelType() {
+        when:
+        connection.model(TestBuild.class)
+
+        then:
+        UnsupportedVersionException e = thrown()
+        e.message == 'Model of type \'TestBuild\' is not supported.'
+    }
+
+    def closeStopsBackingConnection() {
+        when:
+        connection.close()
+
+        then:
+        1 * protocolConnection.stop()
+    }
+}
+
+interface TestBuild extends Project {
+    
+}
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultToolingImplementationLoaderTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultToolingImplementationLoaderTest.groovy
index 262fb17..f693057 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultToolingImplementationLoaderTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultToolingImplementationLoaderTest.groovy
@@ -16,23 +16,67 @@
 package org.gradle.tooling.internal.consumer
 
 import org.gradle.api.internal.AbstractClassPathProvider
-import org.gradle.tooling.internal.provider.DefaultConnectionFactory
-import spock.lang.Specification
 import org.gradle.messaging.actor.ActorFactory
+import org.gradle.tooling.UnsupportedVersionException
+import org.gradle.util.GradleVersion
+import org.slf4j.Logger
+import spock.lang.Specification
+import java.util.regex.Pattern
+import org.gradle.util.TemporaryFolder
+import org.junit.Rule
 
 class DefaultToolingImplementationLoaderTest extends Specification {
+    @Rule public final TemporaryFolder tmpDir = new TemporaryFolder()
     final Distribution distribution = Mock()
-    final DefaultToolingImplementationLoader loader = new DefaultToolingImplementationLoader()
-    
+
     def usesMetaInfServiceToDetermineFactoryImplementation() {
+        given:
+        def loader = new DefaultToolingImplementationLoader()
+        distribution.toolingImplementationClasspath >> ([
+                getToolingApiResourcesDir(),
+                AbstractClassPathProvider.getClasspathForClass(TestConnection.class),
+                AbstractClassPathProvider.getClasspathForClass(ActorFactory.class),
+                AbstractClassPathProvider.getClasspathForClass(Logger.class),
+                getVersionResourcesDir(),
+                AbstractClassPathProvider.getClasspathForClass(GradleVersion.class)
+        ] as Set)
+
         when:
         def factory = loader.create(distribution)
 
         then:
-        factory.class != DefaultConnectionFactory.class
-        factory.class.name == DefaultConnectionFactory.class.name
-        _ * distribution.toolingImplementationClasspath >> ([AbstractClassPathProvider.getClasspathForClass(DefaultConnectionFactory.class),
-                AbstractClassPathProvider.getClasspathForClass(ActorFactory.class)
-        ] as Set)
+        factory.class != TestConnection.class
+        factory.class.name == TestConnection.class.name
+    }
+
+    private getToolingApiResourcesDir() {
+        tmpDir.file("META-INF/services/org.gradle.tooling.internal.protocol.ConnectionVersion4") << TestConnection.name
+        return tmpDir.dir;
+    }
+
+    private getVersionResourcesDir() {
+        return getResourcesDir("org/gradle/version.properties")
+    }
+
+    private getResourcesDir(String name) {
+        def resource = getClass().classLoader.getResource(name)
+        assert resource
+        assert resource.protocol == 'file'
+        def dir = resource.path.replaceFirst(Pattern.quote(name), '')
+        return new File(dir)
+    }
+
+    def failsWhenNoImplementationDeclared() {
+        ClassLoader cl = new ClassLoader() {}
+        def loader = new DefaultToolingImplementationLoader(cl)
+
+        when:
+        loader.create(distribution)
+
+        then:
+        UnsupportedVersionException e = thrown()
+        e.message == "The specified <dist-display-name> is not supported by this tooling API version (${GradleVersion.current().version}, protocol version 4)"
+        _ * distribution.toolingImplementationClasspath >> ([] as Set)
+        _ * distribution.displayName >> '<dist-display-name>'
     }
 }
\ No newline at end of file
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DistributionFactoryTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DistributionFactoryTest.groovy
new file mode 100644
index 0000000..34b7cf3
--- /dev/null
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DistributionFactoryTest.groovy
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer
+
+import org.gradle.logging.ProgressLogger
+import org.gradle.logging.ProgressLoggerFactory
+import org.gradle.util.TemporaryFolder
+import org.gradle.util.TestFile
+import org.junit.Rule
+import spock.lang.Specification
+import org.gradle.util.DistributionLocator
+import org.gradle.util.GradleVersion
+
+class DistributionFactoryTest extends Specification {
+    @Rule final TemporaryFolder tmpDir = new TemporaryFolder()
+    final ProgressLoggerFactory progressLoggerFactory = Mock()
+    final ProgressLogger progressLogger = Mock()
+    final DistributionFactory factory = new DistributionFactory(tmpDir.file('userHome'), progressLoggerFactory)
+
+    def setup() {
+        _ * progressLoggerFactory.newOperation(!null) >> progressLogger
+    }
+
+    def usesTheWrapperPropertiesToDetermineTheDefaultDistribution() {
+        def zipFile = createZip { }
+        tmpDir.file('gradle/wrapper/gradle-wrapper.properties') << "distributionUrl=${zipFile.toURI()}"
+
+        expect:
+        factory.getDefaultDistribution(tmpDir.dir).displayName == "Gradle distribution '${zipFile.toURI()}'"
+    }
+
+    def usesTheCurrentVersionAsTheDefaultDistributionWhenNoWrapperPropertiesFilePresent() {
+        def uri = new DistributionLocator().getDistributionFor(GradleVersion.current())
+
+        expect:
+        factory.getDefaultDistribution(tmpDir.dir).displayName == "Gradle distribution '${uri}'"
+    }
+
+    def createsADisplayNameForAnInstallation() {
+        expect:
+        factory.getDistribution(tmpDir.dir).displayName == "Gradle installation '${tmpDir.dir}'"
+    }
+
+    def usesContentsOfInstallationLibDirectoryAsImplementationClasspath() {
+        def libA = tmpDir.createFile("lib/a.jar")
+        def libB = tmpDir.createFile("lib/b.jar")
+
+        expect:
+        def dist = factory.getDistribution(tmpDir.dir)
+        dist.toolingImplementationClasspath == [libA, libB] as Set
+    }
+
+    def failsWhenInstallationDirectoryDoesNotExist() {
+        TestFile distDir = tmpDir.file('unknown')
+        def dist = factory.getDistribution(distDir)
+
+        when:
+        dist.toolingImplementationClasspath
+
+        then:
+        IllegalArgumentException e = thrown()
+        e.message == "The specified Gradle installation directory '$distDir' does not exist."
+    }
+
+    def failsWhenInstallationDirectoryIsAFile() {
+        TestFile distDir = tmpDir.createFile('dist')
+        def dist = factory.getDistribution(distDir)
+
+        when:
+        dist.toolingImplementationClasspath
+
+        then:
+        IllegalArgumentException e = thrown()
+        e.message == "The specified Gradle installation directory '$distDir' is not a directory."
+    }
+
+    def failsWhenInstallationDirectoryDoesNotContainALibDirectory() {
+        TestFile distDir = tmpDir.createDir('dist')
+        def dist = factory.getDistribution(distDir)
+
+        when:
+        dist.toolingImplementationClasspath
+
+        then:
+        IllegalArgumentException e = thrown()
+        e.message == "The specified Gradle installation directory '$distDir' does not appear to contain a Gradle distribution."
+    }
+
+    def createsADisplayNameForADistribution() {
+        def zipFile = createZip { }
+
+        expect:
+        factory.getDistribution(zipFile.toURI()).displayName == "Gradle distribution '${zipFile.toURI()}'"
+    }
+
+    def usesContentsOfDistributionZipLibDirectoryAsImplementationClasspath() {
+        def zipFile = createZip {
+            lib {
+                file("a.jar")
+                file("b.jar")
+            }
+        }
+        def dist = factory.getDistribution(zipFile.toURI())
+
+        expect:
+        dist.toolingImplementationClasspath.collect { it.name } as Set == ['a.jar', 'b.jar'] as Set
+    }
+
+    def reportsZipDownload() {
+        def zipFile = createZip {
+            lib {
+                file("a.jar")
+            }
+        }
+        def dist = factory.getDistribution(zipFile.toURI())
+
+        when:
+        dist.toolingImplementationClasspath
+
+        then:
+        1 * progressLoggerFactory.newOperation(DistributionFactory.class) >> progressLogger
+        1 * progressLogger.setDescription("Download ${zipFile.toURI()}")
+        1 * progressLogger.started()
+        1 * progressLogger.completed()
+        0 * _._
+    }
+
+    def failsWhenDistributionZipDoesNotExist() {
+        URI zipFile = new URI("http://gradle.org/does-not-exist/gradle-1.0.zip")
+        def dist = factory.getDistribution(zipFile)
+
+        when:
+        dist.toolingImplementationClasspath
+
+        then:
+        IllegalArgumentException e = thrown()
+        e.message == "The specified Gradle distribution '${zipFile}' does not exist."
+    }
+
+    def failsWhenDistributionZipDoesNotContainALibDirectory() {
+        TestFile zipFile = createZip { file("other") }
+        def dist = factory.getDistribution(zipFile.toURI())
+
+        when:
+        dist.toolingImplementationClasspath
+
+        then:
+        IllegalArgumentException e = thrown()
+        e.message == "The specified Gradle distribution '${zipFile.toURI()}' does not appear to contain a Gradle distribution."
+    }
+
+    private TestFile createZip(Closure cl) {
+        def distDir = tmpDir.createDir('dist')
+        distDir.create {
+            "dist-0.9" {
+                cl.delegate = delegate
+                cl.call()
+            }
+        }
+        def zipFile = tmpDir.file("dist-0.9.zip")
+        distDir.zipTo(zipFile)
+        return zipFile
+    }
+
+}
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/LazyConnectionTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/LazyConnectionTest.groovy
new file mode 100644
index 0000000..84b1a64
--- /dev/null
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/LazyConnectionTest.groovy
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer
+
+import org.gradle.tooling.internal.protocol.BuildOperationParametersVersion1
+import org.gradle.tooling.internal.protocol.BuildParametersVersion1
+import org.gradle.tooling.internal.protocol.ConnectionVersion4
+import org.gradle.tooling.internal.protocol.ProjectVersion3
+import spock.lang.Specification
+
+class LazyConnectionTest extends Specification {
+    final Distribution distribution = Mock()
+    final ToolingImplementationLoader implementationLoader = Mock()
+    final BuildParametersVersion1 buildParams = Mock()
+    final BuildOperationParametersVersion1 params = Mock()
+    final ConnectionVersion4 connectionImpl = Mock()
+    final LazyConnection connection = new LazyConnection(distribution, implementationLoader)
+
+    def createsConnectionOnDemandToExecuteBuild() {
+        when:
+        connection.executeBuild(buildParams, params)
+
+        then:
+        1 * implementationLoader.create(distribution) >> connectionImpl
+        1 * connectionImpl.executeBuild(buildParams, params)
+        0 * _._
+    }
+
+    def createsConnectionOnDemandToBuildModel() {
+        when:
+        connection.getModel(ProjectVersion3, params)
+
+        then:
+        1 * implementationLoader.create(distribution) >> connectionImpl
+        1 * connectionImpl.getModel(ProjectVersion3, params)
+        0 * _._
+    }
+
+    def reusesConnection() {
+        when:
+        connection.getModel(ProjectVersion3, params)
+        connection.executeBuild(buildParams, params)
+
+        then:
+        1 * implementationLoader.create(distribution) >> connectionImpl
+        1 * connectionImpl.getModel(ProjectVersion3, params)
+        1 * connectionImpl.executeBuild(buildParams, params)
+        0 * _._
+    }
+
+    def stopsConnectionOnStop() {
+        when:
+        connection.getModel(ProjectVersion3, params)
+        connection.stop()
+
+        then:
+        1 * implementationLoader.create(distribution) >> connectionImpl
+        1 * connectionImpl.getModel(ProjectVersion3, params)
+        1 * connectionImpl.stop()
+        0 * _._
+    }
+
+    def doesNotStopConnectionOnStopIfNotCreated() {
+        when:
+        connection.stop()
+
+        then:
+        0 * _._
+    }
+
+    def doesNotStopConnectionOnStopIfConnectionCouldNotBeCreated() {
+        def failure = new RuntimeException()
+
+        when:
+        connection.getModel(ProjectVersion3, params)
+
+        then:
+        RuntimeException e = thrown()
+        e == failure
+        1 * implementationLoader.create(distribution) >> { throw failure }
+
+        when:
+        connection.stop()
+
+        then:
+        0 * _._
+    }
+}
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/ProgressListenerAdapterTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/ProgressListenerAdapterTest.groovy
new file mode 100644
index 0000000..2936cb8
--- /dev/null
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/ProgressListenerAdapterTest.groovy
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer
+
+import spock.lang.Specification
+import org.gradle.tooling.ProgressListener
+
+class ProgressListenerAdapterTest extends Specification {
+    final ProgressListener listener = Mock()
+    final ProgressListenerAdapter adapter = new ProgressListenerAdapter()
+
+    def setup() {
+        adapter.add(listener)
+    }
+
+    def notifiesListenerOnOperationStartAndEnd() {
+        when:
+        adapter.onOperationStart('main')
+
+        then:
+        1 * listener.statusChanged({it.description == 'main'})
+
+        when:
+        adapter.onOperationEnd()
+
+        then:
+        1 * listener.statusChanged({it.description == ''})
+        0 * _._
+    }
+
+    def notifiesListenerOnNestedOperationStartAndEnd() {
+        when:
+        adapter.onOperationStart('main')
+        adapter.onOperationStart('nested')
+
+        then:
+        1 * listener.statusChanged({it.description == 'main'})
+        1 * listener.statusChanged({it.description == 'nested'})
+
+        when:
+        adapter.onOperationEnd()
+        adapter.onOperationEnd()
+
+        then:
+        1 * listener.statusChanged({it.description == 'main'})
+        1 * listener.statusChanged({it.description == ''})
+        0 * _._
+
+    }
+}
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/ProgressLoggingConnectionTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/ProgressLoggingConnectionTest.groovy
new file mode 100644
index 0000000..4de41c7
--- /dev/null
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/ProgressLoggingConnectionTest.groovy
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer
+
+import org.gradle.listener.ListenerManager
+import org.gradle.logging.ProgressLogger
+import org.gradle.logging.ProgressLoggerFactory
+import org.gradle.tooling.internal.protocol.BuildOperationParametersVersion1
+import org.gradle.tooling.internal.protocol.BuildParametersVersion1
+import org.gradle.tooling.internal.protocol.ConnectionVersion4
+import org.gradle.tooling.internal.protocol.ProjectVersion3
+import spock.lang.Specification
+import org.gradle.tooling.internal.protocol.ProgressListenerVersion1
+
+class ProgressLoggingConnectionTest extends Specification {
+    final ConnectionVersion4 target = Mock()
+    final BuildOperationParametersVersion1 params = Mock()
+    final ProgressListenerVersion1 listener = Mock()
+    final ProgressLogger progressLogger = Mock()
+    final ProgressLoggerFactory progressLoggerFactory = Mock()
+    final ListenerManager listenerManager = Mock()
+    final ProgressLoggingConnection connection = new ProgressLoggingConnection(target, progressLoggerFactory, listenerManager)
+
+    def notifiesProgressListenerOfStartAndEndOfFetchingModel() {
+        when:
+        connection.getModel(ProjectVersion3, params)
+
+        then:
+        1 * listenerManager.addListener(!null)
+        1 * progressLoggerFactory.newOperation(ProgressLoggingConnection.class) >> progressLogger
+        1 * progressLogger.setDescription('Load projects')
+        1 * progressLogger.started()
+        1 * target.getModel(ProjectVersion3, params)
+        1 * progressLogger.completed()
+        1 * listenerManager.removeListener(!null)
+        _ * params.progressListener >> listener
+        0 * _._
+    }
+
+    def notifiesProgressListenerOfStartAndEndOfExecutingBuild() {
+        BuildParametersVersion1 buildParams = Mock()
+
+        when:
+        connection.executeBuild(buildParams, params)
+
+        then:
+        1 * listenerManager.addListener(!null)
+        1 * progressLoggerFactory.newOperation(ProgressLoggingConnection.class) >> progressLogger
+        1 * progressLogger.setDescription('Execute build')
+        1 * progressLogger.started()
+        1 * target.executeBuild(buildParams, params)
+        1 * progressLogger.completed()
+        1 * listenerManager.removeListener(!null)
+        _ * params.progressListener >> listener
+        0 * _._
+    }
+}
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/ProtocolToModelAdapterTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/ProtocolToModelAdapterTest.groovy
index bbd1802..871fcf2 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/ProtocolToModelAdapterTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/ProtocolToModelAdapterTest.groovy
@@ -29,7 +29,7 @@ class ProtocolToModelAdapterTest extends Specification {
         adapter.adapt(TestModel.class, protocolModel) instanceof TestModel
     }
 
-    def proxiesAreEqualWhenTargetObjectsAreEqual() {
+    def proxiesAreEqualWhenTargetProtocolObjectsAreEqual() {
         TestProtocolModel protocolModel1 = Mock()
         TestProtocolModel protocolModel2 = Mock()
 
@@ -97,6 +97,32 @@ class ProtocolToModelAdapterTest extends Specification {
         model.project.is(model.project)
         model.children.is(model.children)
     }
+
+    def reportsMethodWhichDoesNotExistOnProtocolObject() {
+        PartialTestProtocolModel protocolModel = Mock()
+
+        when:
+        def model = adapter.adapt(TestModel.class, protocolModel)
+        model.project
+
+        then:
+        UnsupportedOperationException e = thrown()
+        e.message == "Cannot map method TestModel.getProject() to target object of type ${protocolModel.class.simpleName}."
+    }
+
+    def propagatesExceptionThrownByProtocolObject() {
+        TestProtocolModel protocolModel = Mock()
+        RuntimeException failure = new RuntimeException()
+
+        when:
+        def model = adapter.adapt(TestModel.class, protocolModel)
+        model.name
+
+        then:
+        protocolModel.name >> { throw failure }
+        RuntimeException e = thrown()
+        e == failure
+    }
 }
 
 interface TestModel {
@@ -119,6 +145,10 @@ interface TestProtocolModel {
     Iterable<? extends TestProtocolProject> getChildren()
 }
 
+interface PartialTestProtocolModel {
+    String getName()
+}
+
 interface TestProtocolProject {
     String getName()
 }
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/TestConnection.java b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/TestConnection.java
new file mode 100644
index 0000000..fa3e48a
--- /dev/null
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/TestConnection.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer;
+
+import org.gradle.tooling.internal.protocol.*;
+
+public class TestConnection implements ConnectionVersion4 {
+    public void executeBuild(BuildParametersVersion1 buildParameters, BuildOperationParametersVersion1 operationParameters) throws IllegalStateException {
+        throw new UnsupportedOperationException();
+    }
+
+    public void stop() {
+        throw new UnsupportedOperationException();
+    }
+
+    public ConnectionMetaDataVersion1 getMetaData() {
+        throw new UnsupportedOperationException();
+    }
+
+    public ProjectVersion3 getModel(Class<? extends ProjectVersion3> type, BuildOperationParametersVersion1 operationParameters) throws UnsupportedOperationException, IllegalStateException {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/subprojects/tooling-api/tooling-api.gradle b/subprojects/tooling-api/tooling-api.gradle
index 1de2c4e..03aaeb0 100644
--- a/subprojects/tooling-api/tooling-api.gradle
+++ b/subprojects/tooling-api/tooling-api.gradle
@@ -3,7 +3,7 @@ apply plugin: 'groovy'
 dependencies {
     groovy libraries.groovy_depends
 
-    compile project(':core'), project(':wrapper')
+    publishCompile project(':core'), project(':wrapper')
 
     testCompile project(path: ':core', configuration: 'testFixtures')
     testRuntime project(path: ':core', configuration: 'testFixturesRuntime')
diff --git a/subprojects/ui/src/integTest/groovy/org/gradle/integtests/LiveOutputIntegrationTest.groovy b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/LiveOutputIntegrationTest.groovy
index a637006..2fa00f4 100644
--- a/subprojects/ui/src/integTest/groovy/org/gradle/integtests/LiveOutputIntegrationTest.groovy
+++ b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/LiveOutputIntegrationTest.groovy
@@ -15,6 +15,10 @@
 */
 package org.gradle.integtests
 
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.locks.Condition
+import java.util.concurrent.locks.Lock
+import java.util.concurrent.locks.ReentrantLock
 import org.gradle.foundation.TestUtility
 import org.gradle.foundation.ipc.gradle.ExecuteGradleCommandServerProtocol
 import org.gradle.gradleplugin.foundation.GradlePluginLord
@@ -26,10 +30,6 @@ import org.junit.Assert
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
-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.
@@ -65,10 +65,6 @@ that's likely to change over time. This version executes the command via GradleP
 
     @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
 
@@ -95,9 +91,6 @@ that's likely to change over time. This version executes the command via GradleR
 */
     @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
 
diff --git a/subprojects/ui/src/integTest/groovy/org/gradle/integtests/MultiprojectProjectAndTaskListIntegrationTest.groovy b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/MultiprojectProjectAndTaskListIntegrationTest.groovy
index 8b583d7..a10efe8 100644
--- a/subprojects/ui/src/integTest/groovy/org/gradle/integtests/MultiprojectProjectAndTaskListIntegrationTest.groovy
+++ b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/MultiprojectProjectAndTaskListIntegrationTest.groovy
@@ -29,6 +29,7 @@ import org.junit.Rule
 import org.junit.Test
 import java.util.concurrent.TimeUnit
 import org.gradle.foundation.TestUtility
+import org.gradle.util.TestFile
 
 /**
  This tests the multiproject sample with the GradleView mechanism.
@@ -43,7 +44,7 @@ class MultiprojectProjectAndTaskListIntegrationTest {
     static final String SERVICES_NAME = 'services'
     static final String WEBAPP_PATH = "$SERVICES_NAME/$WEBAPP_NAME" as String
 
-    private File javaprojectDir
+    private TestFile javaprojectDir
 
     @Rule public final GradleDistribution dist = new GradleDistribution()
     @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
@@ -66,14 +67,8 @@ class MultiprojectProjectAndTaskListIntegrationTest {
 
     @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.setCurrentDirectory(javaprojectDir);
         gradlePluginLord.setGradleHomeDirectory(dist.gradleHomeDir);
 
         //refresh the projects and wait. This will throw an exception if it fails.
@@ -117,14 +112,8 @@ class MultiprojectProjectAndTaskListIntegrationTest {
    @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.setCurrentDirectory(javaprojectDir);
         gradlePluginLord.setGradleHomeDirectory(dist.gradleHomeDir);
 
         GradleInterfaceWrapperVersion1 wrapper = new GradleInterfaceWrapperVersion1( gradlePluginLord );
@@ -178,13 +167,8 @@ class MultiprojectProjectAndTaskListIntegrationTest {
    @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.setCurrentDirectory(javaprojectDir);
       gradlePluginLord.setGradleHomeDirectory(dist.gradleHomeDir);
 
       //refresh the projects and wait. This will throw an exception if it fails.
@@ -216,13 +200,8 @@ class MultiprojectProjectAndTaskListIntegrationTest {
    @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.setCurrentDirectory(javaprojectDir);
       gradlePluginLord.setGradleHomeDirectory(dist.gradleHomeDir);
 
       //refresh the projects and wait. This will throw an exception if it fails.
diff --git a/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/ExecuteGradleCommandClientProtocol.java b/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/ExecuteGradleCommandClientProtocol.java
index cd8ec82..2a830ff 100644
--- a/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/ExecuteGradleCommandClientProtocol.java
+++ b/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/ExecuteGradleCommandClientProtocol.java
@@ -183,9 +183,8 @@ public class ExecuteGradleCommandClientProtocol implements ClientProcess.Protoco
          * @param output The text.
          */
         public synchronized void onOutput(CharSequence output) {
-            String text = output.toString();
-            this.allOutputText.append(text);
-            this.bufferedLiveOutput.append(text);
+            this.allOutputText.append(output);
+            this.bufferedLiveOutput.append(output);
         }
 
         /**
diff --git a/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/TaskListClientProtocol.java b/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/TaskListClientProtocol.java
index 030ff09..bfa894c 100644
--- a/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/TaskListClientProtocol.java
+++ b/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/TaskListClientProtocol.java
@@ -71,8 +71,7 @@ public class TaskListClientProtocol implements ClientProcess.Protocol {
         }
 
         public synchronized void onOutput(CharSequence output) {
-            String text = output.toString();
-            allOutputText.append(text);
+            allOutputText.append(output);
         }
 
         /**
diff --git a/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/foundation/GradleInterfaceWrapperVersion1.java b/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/foundation/GradleInterfaceWrapperVersion1.java
index 02e5cfd..82a78fe 100644
--- a/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/foundation/GradleInterfaceWrapperVersion1.java
+++ b/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/foundation/GradleInterfaceWrapperVersion1.java
@@ -48,7 +48,7 @@ public class GradleInterfaceWrapperVersion1 implements GradleInterfaceVersion1 {
      * @return the version of gradle being run. This is basically the version from the jar file.
      */
     public String getVersion() {
-        return new GradleVersion().getVersion();
+        return GradleVersion.current().getVersion();
     }
 
     /**
diff --git a/subprojects/ui/src/test/groovy/org/gradle/foundation/TestUtility.java b/subprojects/ui/src/test/groovy/org/gradle/foundation/TestUtility.java
index 9648f71..6c8a1b8 100644
--- a/subprojects/ui/src/test/groovy/org/gradle/foundation/TestUtility.java
+++ b/subprojects/ui/src/test/groovy/org/gradle/foundation/TestUtility.java
@@ -35,6 +35,7 @@ import java.io.File;
 import java.util.*;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * Utility class for initializing various test objects related.
@@ -335,6 +336,7 @@ public class TestUtility {
         gradlePluginLord.startExecutionQueue();   //make sure its started
 
         final CountDownLatch complete = new CountDownLatch(1);
+        final AtomicReference<String> errorOutput = new AtomicReference<String>();
 
         GradlePluginLord.RequestObserver observer = new GradlePluginLord.RequestObserver() {
             public void executionRequestAdded(ExecutionRequest request) {
@@ -348,12 +350,14 @@ public class TestUtility {
             }
 
             public void requestExecutionComplete(Request request, int result, String output) {
+                if (result != 0) {
+                    errorOutput.set(output);
+                }
                 complete.countDown();
             }
         };
 
-        gradlePluginLord.addRequestObserver(observer,
-                false);   //add the observer before we add the request due to timing issues. It's possible for it to completely execute before we return from addRefreshRequestToQueue.
+        gradlePluginLord.addRequestObserver(observer, false);   //add the observer before we add the request due to timing issues. It's possible for it to completely execute before we return from addRefreshRequestToQueue.
         Request request = gradlePluginLord.addRefreshRequestToQueue();
 
         //make sure we've got a request
@@ -374,6 +378,9 @@ public class TestUtility {
             request.cancel(); //just to clean up after ourselves a little, cancel the request.
             throw new AssertionFailedError("Failed to complete refresh in alotted time: " + maximumWaitValue + " " + maximumWaitUnits + ". Considering this failed.");
         }
+        if (errorOutput.get() != null) {
+            throw new AssertionFailedError(String.format("Command failed with output:%n%s", errorOutput.get()));
+        }
     }
 
     /**
diff --git a/subprojects/ui/ui.gradle b/subprojects/ui/ui.gradle
index 81efd51..babeb74 100644
--- a/subprojects/ui/ui.gradle
+++ b/subprojects/ui/ui.gradle
@@ -34,15 +34,6 @@ dependencies {
     integTestRuntime project(path: ':core', configuration: 'integTestFixturesRuntime')
 }
 
-task integTest(type: Test, dependsOn: [ rootProject.intTestImage ]) {
-    systemProperties['integTest.gradleHomeDir'] = rootProject.intTestImage.integTestGradleHome.absolutePath
-    systemProperties['integTest.gradleUserHomeDir'] = rootProject.integTest.integTestUserDir.absolutePath
-    testClassesDir = sourceSets.integTest.classesDir
-    classpath = sourceSets.integTest.runtimeClasspath
-
-    doFirst { task -> systemProperties['integTest.gradleHomeDir'] = rootProject.intTestImage.integTestGradleHome.absolutePath }
-}
-
 test {
     jvmArgs '-Xms128m', '-Xmx256m', '-XX:MaxPermSize=128m', '-XX:+HeapDumpOnOutOfMemoryError'
 }
diff --git a/subprojects/wrapper/src/main/java/org/gradle/api/tasks/wrapper/Wrapper.java b/subprojects/wrapper/src/main/java/org/gradle/api/tasks/wrapper/Wrapper.java
index 8bb1e63..23d5292 100644
--- a/subprojects/wrapper/src/main/java/org/gradle/api/tasks/wrapper/Wrapper.java
+++ b/subprojects/wrapper/src/main/java/org/gradle/api/tasks/wrapper/Wrapper.java
@@ -22,12 +22,9 @@ import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.tasks.Input;
 import org.gradle.api.tasks.OutputFile;
 import org.gradle.api.tasks.TaskAction;
-import org.gradle.api.tasks.wrapper.internal.DistributionLocator;
+import org.gradle.util.DistributionLocator;
 import org.gradle.api.tasks.wrapper.internal.WrapperScriptGenerator;
-import org.gradle.util.DeprecationLogger;
-import org.gradle.util.GFileUtils;
-import org.gradle.util.GUtil;
-import org.gradle.util.GradleVersion;
+import org.gradle.util.*;
 
 import java.io.File;
 import java.net.URL;
@@ -101,7 +98,7 @@ public class Wrapper extends DefaultTask {
         archiveName = DEFAULT_ARCHIVE_NAME;
         archiveClassifier = DEFAULT_ARCHIVE_CLASSIFIER;
         archivePath = DEFAULT_DISTRIBUTION_PARENT_NAME;
-        gradleVersion = new GradleVersion();
+        gradleVersion = GradleVersion.current();
     }
 
     @TaskAction
@@ -254,7 +251,7 @@ public class Wrapper extends DefaultTask {
      * use for building your project.
      */
     public void setGradleVersion(String gradleVersion) {
-        this.gradleVersion = new GradleVersion(gradleVersion);
+        this.gradleVersion = GradleVersion.version(gradleVersion);
     }
 
     /**
diff --git a/subprojects/wrapper/src/main/java/org/gradle/api/tasks/wrapper/internal/DistributionLocator.java b/subprojects/wrapper/src/main/java/org/gradle/api/tasks/wrapper/internal/DistributionLocator.java
deleted file mode 100644
index 75fb740..0000000
--- a/subprojects/wrapper/src/main/java/org/gradle/api/tasks/wrapper/internal/DistributionLocator.java
+++ /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.tasks.wrapper.internal;
-
-import org.gradle.util.GradleVersion;
-
-public class DistributionLocator {
-    private static final String ARTIFACTORY_RELEASE_REPOSITORY = "http://gradle.artifactoryonline.com/gradle/distributions";
-    private static final String ARTIFACTORY_SNAPSHOT_REPOSITORY = "http://gradle.artifactoryonline.com/gradle/distributions/gradle-snapshots";
-    private static final String CODEHAUS_RELEASE_REPOSITORY = "http://dist.codehaus.org/gradle";
-    private static final String CODEHAUS_SNAPSHOT_REPOSITORY = "http://snapshots.dist.codehaus.org/gradle";
-
-    public String getDistributionFor(GradleVersion version) {
-        return getDistribution(getDistributionRepository(version), version, "gradle", "bin");
-    }
-
-    public String getDistributionRepository(GradleVersion version) {
-        if (version.compareTo(new GradleVersion("0.9")) >= 0) {
-            if (version.isSnapshot()) {
-                return ARTIFACTORY_SNAPSHOT_REPOSITORY;
-            }
-            return ARTIFACTORY_RELEASE_REPOSITORY;
-        } else {
-            if (version.isSnapshot()) {
-                return CODEHAUS_SNAPSHOT_REPOSITORY;
-            }
-            return CODEHAUS_RELEASE_REPOSITORY;
-        }
-    }
-
-    public String getDistribution(String repositoryUrl, GradleVersion version, String archiveName,
-                                  String archiveClassifier) {
-        return String.format("%s/%s-%s-%s.zip", repositoryUrl, archiveName, version.getVersion(), archiveClassifier);
-    }
-}
diff --git a/subprojects/wrapper/src/main/java/org/gradle/wrapper/Install.java b/subprojects/wrapper/src/main/java/org/gradle/wrapper/Install.java
index b56f6cb..b489695 100644
--- a/subprojects/wrapper/src/main/java/org/gradle/wrapper/Install.java
+++ b/subprojects/wrapper/src/main/java/org/gradle/wrapper/Install.java
@@ -16,6 +16,8 @@
 
 package org.gradle.wrapper;
 
+import org.gradle.util.SystemProperties;
+
 import java.io.*;
 import java.net.URI;
 import java.util.Enumeration;
@@ -61,7 +63,7 @@ public class Install {
         unzip(localZipFile, distDest);
         if (!gradleHome.isDirectory()) {
             throw new RuntimeException(String.format(
-                    "Gradle distribution '%s' does not contain expected directory '%s'.", distributionUrl,
+                    "Gradle distribution '%s' does not contain expected root directory '%s'.", distributionUrl,
                     gradleHome.getName()));
         }
         setExecutablePermissions(gradleHome);
@@ -84,7 +86,7 @@ public class Install {
                 errorMessage = "";
                 String line;
                 while ((line = is.readLine()) != null) {
-                    errorMessage += line + System.getProperty("line.separator");
+                    errorMessage += line + SystemProperties.getLineSeparator();
                 }
             }
         } catch (IOException e) {
diff --git a/subprojects/wrapper/src/main/java/org/gradle/wrapper/Wrapper.java b/subprojects/wrapper/src/main/java/org/gradle/wrapper/Wrapper.java
index 18a6a0e..1cd74f1 100644
--- a/subprojects/wrapper/src/main/java/org/gradle/wrapper/Wrapper.java
+++ b/subprojects/wrapper/src/main/java/org/gradle/wrapper/Wrapper.java
@@ -15,9 +15,7 @@
  */
 package org.gradle.wrapper;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStream;
+import java.io.*;
 import java.net.URI;
 import java.util.Properties;
 
@@ -26,41 +24,74 @@ import java.util.Properties;
  */
 public class Wrapper {
     public static final String WRAPPER_PROPERTIES_PROPERTY = "org.gradle.wrapper.properties";
-    
+
     public static final String DISTRIBUTION_URL_PROPERTY = "distributionUrl";
     public static final String DISTRIBUTION_BASE_PROPERTY = "distributionBase";
     public static final String ZIP_STORE_BASE_PROPERTY = "zipStoreBase";
     public static final String DISTRIBUTION_PATH_PROPERTY = "distributionPath";
     public static final String ZIP_STORE_PATH_PROPERTY = "zipStorePath";
+    private final Properties properties;
+    private final URI distribution;
+    private final File propertiesFile;
 
-    public void execute(String[] args, Install install, BootstrapMainStarter bootstrapMainStarter) throws Exception {
-        Properties wrapperProperties = new Properties();
-        InputStream inStream = new FileInputStream(getWrapperPropertiesFile());
+    public Wrapper() {
+        this(new File(System.getProperty(WRAPPER_PROPERTIES_PROPERTY)), new Properties());
+    }
+
+    public Wrapper(File projectDir) {
+        this(new File(projectDir, "gradle/wrapper/gradle-wrapper.properties"), new Properties());
+    }
+
+    private Wrapper(File propertiesFile, Properties properties) {
+        this.properties = properties;
+        this.propertiesFile = propertiesFile;
+        if (propertiesFile.exists()) {
+            try {
+                loadProperties(propertiesFile, properties);
+                distribution = new URI(getProperty(DISTRIBUTION_URL_PROPERTY));
+            } catch (Exception e) {
+                throw new RuntimeException(String.format("Could not load wrapper properties from '%s'.", propertiesFile), e);
+            }
+        } else {
+            distribution = null;
+        }
+    }
+
+    private static void loadProperties(File propertiesFile, Properties properties) throws IOException {
+        InputStream inStream = new FileInputStream(propertiesFile);
         try {
-            wrapperProperties.load(inStream);
+            properties.load(inStream);
         } finally {
             inStream.close();
         }
+    }
+
+    /**
+     * Returns the distribution which this wrapper will use. Returns null if no wrapper meta-data was found in the specified project directory.
+     */
+    public URI getDistribution() {
+        return distribution;
+    }
+
+    public void execute(String[] args, Install install, BootstrapMainStarter bootstrapMainStarter) throws Exception {
+        if (distribution == null) {
+            throw new FileNotFoundException(String.format("Wrapper properties file '%s' does not exist.", propertiesFile));
+        }
         File gradleHome = install.createDist(
-                new URI(getProperty(wrapperProperties, DISTRIBUTION_URL_PROPERTY)),
-                getProperty(wrapperProperties, DISTRIBUTION_BASE_PROPERTY),
-                getProperty(wrapperProperties, DISTRIBUTION_PATH_PROPERTY),
-                getProperty(wrapperProperties, ZIP_STORE_BASE_PROPERTY),
-                getProperty(wrapperProperties, ZIP_STORE_PATH_PROPERTY)
+                getDistribution(),
+                getProperty(DISTRIBUTION_BASE_PROPERTY),
+                getProperty(DISTRIBUTION_PATH_PROPERTY),
+                getProperty(ZIP_STORE_BASE_PROPERTY),
+                getProperty(ZIP_STORE_PATH_PROPERTY)
         );
         bootstrapMainStarter.start(args, gradleHome);
     }
 
-    private File getWrapperPropertiesFile() {
-        return new File(System.getProperty(WRAPPER_PROPERTIES_PROPERTY));
-    }
-
-    private String getProperty(Properties wrapperProperties, String propertyName) {
-        String value = wrapperProperties.getProperty(propertyName);
+    private String getProperty(String propertyName) {
+        String value = properties.getProperty(propertyName);
         if (value == null) {
             throw new RuntimeException(String.format(
-                    "No value with key '%s' specified in wrapper properties file '%s'.", propertyName,
-                    getWrapperPropertiesFile()));
+                    "No value with key '%s' specified in wrapper properties file '%s'.", propertyName, propertiesFile));
         }
         return value;
     }
diff --git a/subprojects/wrapper/src/test/groovy/org/gradle/api/tasks/wrapper/WrapperTest.java b/subprojects/wrapper/src/test/groovy/org/gradle/api/tasks/wrapper/WrapperTest.java
index d9c392f..d868c32 100644
--- a/subprojects/wrapper/src/test/groovy/org/gradle/api/tasks/wrapper/WrapperTest.java
+++ b/subprojects/wrapper/src/test/groovy/org/gradle/api/tasks/wrapper/WrapperTest.java
@@ -81,7 +81,7 @@ public class WrapperTest extends AbstractTaskTest {
         assertEquals(toNative("gradle/wrapper"), wrapper.getJarPath());
         assertEquals(new File(getProject().getProjectDir(), "gradlew"), wrapper.getScriptFile());
         assertEquals(".", wrapper.getScriptDestinationPath());
-        assertEquals(new GradleVersion().getVersion(), wrapper.getGradleVersion());
+        assertEquals(GradleVersion.current().getVersion(), wrapper.getGradleVersion());
         assertEquals(Wrapper.DEFAULT_DISTRIBUTION_PARENT_NAME, wrapper.getDistributionPath());
         assertEquals(Wrapper.DEFAULT_ARCHIVE_NAME, wrapper.getArchiveName());
         assertEquals(Wrapper.DEFAULT_ARCHIVE_CLASSIFIER, wrapper.getArchiveClassifier());
@@ -95,15 +95,15 @@ public class WrapperTest extends AbstractTaskTest {
     @Test
     public void testDownloadsFromReleaseRepositoryForReleaseVersions() {
         wrapper.setGradleVersion("0.9.1");
-        assertEquals("http://gradle.artifactoryonline.com/gradle/distributions", wrapper.getUrlRoot());
-        assertEquals("http://gradle.artifactoryonline.com/gradle/distributions/gradle-0.9.1-bin.zip", wrapper.getDistributionUrl());
+        assertEquals("http://repo.gradle.org/gradle/distributions", wrapper.getUrlRoot());
+        assertEquals("http://repo.gradle.org/gradle/distributions/gradle-0.9.1-bin.zip", wrapper.getDistributionUrl());
     }
 
     @Test
     public void testDownloadsFromReleaseRepositoryForPreviewReleaseVersions() {
         wrapper.setGradleVersion("1.0-milestone-1");
-        assertEquals("http://gradle.artifactoryonline.com/gradle/distributions", wrapper.getUrlRoot());
-        assertEquals("http://gradle.artifactoryonline.com/gradle/distributions/gradle-1.0-milestone-1-bin.zip", wrapper.getDistributionUrl());
+        assertEquals("http://repo.gradle.org/gradle/distributions", wrapper.getUrlRoot());
+        assertEquals("http://repo.gradle.org/gradle/distributions/gradle-1.0-milestone-1-bin.zip", wrapper.getDistributionUrl());
     }
 
     @Test
@@ -116,8 +116,8 @@ public class WrapperTest extends AbstractTaskTest {
     @Test
     public void testDownloadsFromSnapshotRepositoryForSnapshotVersions() {
         wrapper.setGradleVersion("0.9.1-20101224110000+1100");
-        assertEquals("http://gradle.artifactoryonline.com/gradle/distributions/gradle-snapshots", wrapper.getUrlRoot());
-        assertEquals("http://gradle.artifactoryonline.com/gradle/distributions/gradle-snapshots/gradle-0.9.1-20101224110000+1100-bin.zip", wrapper.getDistributionUrl());
+        assertEquals("http://repo.gradle.org/gradle/distributions/gradle-snapshots", wrapper.getUrlRoot());
+        assertEquals("http://repo.gradle.org/gradle/distributions/gradle-snapshots/gradle-0.9.1-20101224110000+1100-bin.zip", wrapper.getDistributionUrl());
     }
 
     @Test
@@ -131,7 +131,7 @@ public class WrapperTest extends AbstractTaskTest {
     public void testUsesExplicitlyDefinedDistributionUrl() {
         wrapper.setGradleVersion("0.9");
         wrapper.setDistributionUrl("http://some-url");
-        assertEquals("http://gradle.artifactoryonline.com/gradle/distributions", wrapper.getUrlRoot());
+        assertEquals("http://repo.gradle.org/gradle/distributions", wrapper.getUrlRoot());
         assertEquals("http://some-url", wrapper.getDistributionUrl());
     }
 
diff --git a/subprojects/wrapper/src/test/groovy/org/gradle/wrapper/WrapperTest.groovy b/subprojects/wrapper/src/test/groovy/org/gradle/wrapper/WrapperTest.groovy
new file mode 100644
index 0000000..e8627ca
--- /dev/null
+++ b/subprojects/wrapper/src/test/groovy/org/gradle/wrapper/WrapperTest.groovy
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.wrapper
+
+import spock.lang.Specification
+import org.gradle.util.TemporaryFolder
+import org.junit.Rule
+import org.gradle.util.TestFile
+import org.gradle.util.SetSystemProperties
+
+class WrapperTest extends Specification {
+    @Rule
+    public final TemporaryFolder tmpDir = new TemporaryFolder();
+    @Rule
+    public final SetSystemProperties systemProperties = new SetSystemProperties();
+    final Install install = Mock()
+    final BootstrapMainStarter start = Mock()
+    TestFile projectDir;
+    TestFile propertiesFile;
+
+    def setup() {
+        projectDir = tmpDir.dir
+        propertiesFile = tmpDir.file('gradle/wrapper/gradle-wrapper.properties')
+        def properties = new Properties()
+        properties.distributionUrl = 'http://server/test/gradle.zip'
+        properties.distributionBase = 'testDistBase'
+        properties.distributionPath = 'testDistPath'
+        properties.zipStoreBase = 'testZipBase'
+        properties.zipStorePath = 'testZipPath'
+        propertiesFile.parentFile.mkdirs()
+        propertiesFile.withOutputStream { properties.store(it, 'header') }
+        System.setProperty(Wrapper.WRAPPER_PROPERTIES_PROPERTY, propertiesFile.absolutePath)
+    }
+
+    def "uses system property to locate properties file"() {
+        def wrapper = new Wrapper()
+
+        expect:
+        wrapper.distribution == new URI('http://server/test/gradle.zip')
+    }
+
+    def "loads wrapper meta data from project directory"() {
+        def wrapper = new Wrapper(projectDir)
+
+        expect:
+        wrapper.distribution == new URI('http://server/test/gradle.zip')
+    }
+
+    def "can query for distribution when properties file does not exist"() {
+        def wrapper = new Wrapper(tmpDir.file('unknown'))
+
+        expect:
+        wrapper.distribution == null
+    }
+
+    def "execute installs distribution and launches application"() {
+        def wrapper = new Wrapper()
+        def installDir = tmpDir.file('install')
+
+        when:
+        wrapper.execute(['arg'] as String[], install, start)
+
+        then:
+        1 * install.createDist(new URI('http://server/test/gradle.zip'), 'testDistBase', 'testDistPath', 'testZipBase', 'testZipPath') >> installDir
+        1 * start.start(['arg'] as String[], installDir)
+        0 * _._
+    }
+
+    def "fails when distribution not specified"() {
+        def properties = new Properties()
+        propertiesFile.withOutputStream { properties.store(it, 'header') }
+
+        when:
+        new Wrapper()
+
+        then:
+        RuntimeException e = thrown()
+        e.message == "Could not load wrapper properties from '$propertiesFile'."
+        e.cause.message == "No value with key 'distributionUrl' specified in wrapper properties file '$propertiesFile'."
+    }
+
+    def "execute fails when properties file does not exist"() {
+        propertiesFile.delete()
+        def wrapper = new Wrapper()
+
+        when:
+        wrapper.execute(['arg'] as String[], install, start)
+
+        then:
+        FileNotFoundException e = thrown()
+        e.message == "Wrapper properties file '$propertiesFile' does not exist."
+    }
+}
diff --git a/subprojects/wrapper/src/test/groovy/org/gradle/wrapper/WrapperTest.java b/subprojects/wrapper/src/test/groovy/org/gradle/wrapper/WrapperTest.java
deleted file mode 100644
index e9eb283..0000000
--- a/subprojects/wrapper/src/test/groovy/org/gradle/wrapper/WrapperTest.java
+++ /dev/null
@@ -1,77 +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.wrapper;
-
-import org.gradle.util.JUnit4GroovyMockery;
-import org.gradle.util.SetSystemProperties;
-import org.gradle.util.TemporaryFolder;
-import org.jmock.Expectations;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.net.URI;
-import java.util.Properties;
-
-/**
- * @author Hans Dockter
- */
- at RunWith(org.jmock.integration.junit4.JMock.class)
-public class WrapperTest {
-    Wrapper wrapper;
-    BootstrapMainStarter bootstrapMainStarterMock;
-    Install installMock;
-    final JUnit4Mockery context = new JUnit4GroovyMockery();
-    @Rule
-    public final SetSystemProperties systemProperties = new SetSystemProperties();
-    @Rule
-    public final TemporaryFolder tmpDir = new TemporaryFolder();
-    private File propertiesDir = tmpDir.getDir();
-    private File propertiesFile = new File(propertiesDir, "wrapper.properties");
-
-    @Before
-    public void setUp() throws IOException {
-        wrapper = new Wrapper();
-        bootstrapMainStarterMock = context.mock(BootstrapMainStarter.class);
-        installMock = context.mock(Install.class);
-        Properties testProperties = new Properties();
-        testProperties.load(WrapperTest.class.getResourceAsStream("/org/gradle/wrapper/wrapper.properties"));
-        testProperties.store(new FileOutputStream(propertiesFile), null);
-        System.setProperty(Wrapper.WRAPPER_PROPERTIES_PROPERTY, propertiesFile.getCanonicalPath());
-    }
-
-    @Test
-    public void execute() throws Exception {
-        final String[] expectedArgs = {"arg1", "arg2"};
-        final File expectedGradleHome = new File("somepath");
-        context.checking(new Expectations() {{
-          one(installMock).createDist(
-                  new URI("http://server/test/gradle.zip"),
-                  "testDistBase",
-                  "testDistPath",
-                  "testZipBase",
-                  "testZipPath"
-          ); will(returnValue(expectedGradleHome));
-          one(bootstrapMainStarterMock).start(expectedArgs, expectedGradleHome);
-        }});
-        wrapper.execute(expectedArgs, installMock, bootstrapMainStarterMock);
-    }
-}
diff --git a/subprojects/wrapper/wrapper.gradle b/subprojects/wrapper/wrapper.gradle
index eefd65e..86b4358 100644
--- a/subprojects/wrapper/wrapper.gradle
+++ b/subprojects/wrapper/wrapper.gradle
@@ -17,9 +17,9 @@ apply plugin: 'groovy'
 
 dependencies {
     groovy libraries.groovy_depends
-    compile project(':core')
+    publishCompile project(':core')
 
-    compile libraries.commons_io, libraries.ant, libraries.ant_nodeps
+    compile libraries.commons_io, libraries.ant
 
     testCompile project(path: ':core', configuration: 'testFixtures')
     testRuntime project(path: ':core', configuration: 'testFixturesRuntime')

-- 
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